Intro

Project overview

In this case study, I am a junior data analyst who is working for the marketing analytics team at Bellabeat, a high-tech company that designs health tracking products for women. This hypothetical scenario is provided by Google’s Data Analytics Certificate Program through Coursera, and I will be outlining the standard data analysis pathway throughout this project (ask, prepare, process, analyze, share, and act).

Ask

Business task

My main objective for this project is the following: Based on trends in non-Bellabeat smart devices for tracking health data, what trends occur among consumers and how can they be applied to a Bellabeat device and the company’s marketing strategy?

Stakeholders

After my analysis, I would share my high-level findings and provide suggestions to improve a Bellabeat device to the main stakeholder, Urška Sršen, one of the two founders of Bellabeat. I would also share my findings and more detailed analyses and code with the data analytics team prior to sharing high-level results with the founder.

Prepare

Non-Bellabeat Data

The data for non-Bellabeat devices was downloaded from Kaggle user Möbius: Fitbit Fitness Tracker Data. This data collection contains 18 CSV files about exercise activity, intensity, calories, steps, METS (metabolic equivalent of task), heartrate, weight, and sleep across different time measurements (days, hours, minutes, seconds).

The original datasets were created by Furberg, R. et al, published on Zenodo in May 2016. It is open-sourced with a Creative Commons Attribution 4.0 International Public License. The data is based on a 2016 crowdsourced survey of Fitbit users, conducted by Amazon Mechanical Turk.

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ──────────────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.5     ✓ purrr   0.3.4
✓ tibble  3.1.5     ✓ dplyr   1.0.7
✓ tidyr   1.1.4     ✓ stringr 1.4.0
✓ readr   2.0.2     ✓ forcats 0.5.1
── Conflicts ─────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

Load datasets

When loading data, I wanted to name the dataframes with meaningful names. To simplify the file names, I used the following key for time measurements:

  • daily - d
  • hourly - h
  • minutes - m
  • seconds - s
  • Narr - Narrow
  • Wide - Wide

Dataframe names begin with a time measurement, followed by a period for readability, and a description of the health measurement.

# I commented out dataframes that won't be used in this analysis.
# To view dataframes, uncomment each View(dataframe_name) and re-run code

# activity
d.activity <- read_csv("./data/dailyActivity_merged.csv", show_col_types = FALSE) 
# View(d.activity)
# d.activity contains the data found separately in d.intensities, d.calories, d.steps

# intensities
# d.intensities <- read_csv("./data/dailyIntensities_merged.csv", show_col_types = FALSE)
# View(d.intensities)
h.intensities <- read_csv("./data/hourlyIntensities_merged.csv", show_col_types = FALSE)
#View(h.intensities)
# m.intensitiesNarr <- read_csv("./data/minuteIntensitiesNarrow_merged.csv", show_col_types = FALSE)
#View(m.intensitiesNarr)
# m.intensitiesWide <- read_csv("./data/minuteIntensitiesWide_merged.csv", show_col_types = FALSE)


# calories
# d.calories <- read_csv("./data/dailyCalories_merged.csv", show_col_types = FALSE)
# View(d.calories)
h.calories <- read_csv("./data/hourlyCalories_merged.csv", show_col_types = FALSE)
# View(h.calories)
# m.caloriesNarr <- read_csv("./data/minuteCaloriesNarrow_merged.csv", show_col_types = FALSE)
# m.caloriesWide <- read_csv("./data/minuteCaloriesWide_merged.csv", show_col_types = FALSE)
# View(m.caloriesWide)

# steps
# d.steps <- read_csv("./data/dailySteps_merged.csv", show_col_types = FALSE)
# View(d.steps)
h.steps <- read_csv("./data/hourlySteps_merged.csv", show_col_types = FALSE)
# View(h.steps)
# m.stepsNarr <- read_csv("./data/minuteStepsNarrow_merged.csv", show_col_types = FALSE)
# View(m.stepsNarr)
# m.stepsWide <- read_csv("./data/minuteStepsWide_merged.csv", show_col_types = FALSE)
# View(m.stepsWide)

# METs - metabolic equivalent of task (relates to intensity)
# minuteMETS <- read_csv("./data/minuteMETsNarrow_merged.csv", show_col_types = FALSE)
# View(minuteMETS)

# heartrate
# s.heartrate <- read_csv("./data/heartrate_seconds_merged.csv", show_col_types = FALSE)

# weight
# weightLog <- read_csv("./data/weightLogInfo_merged.csv", show_col_types = FALSE)
# View(weightLog)

# sleep
sleepDay <- read_csv("./data/sleepDay_merged.csv", show_col_types = FALSE)
# View(sleepDay)

After loading the data in R, I noticed some column values, like distance or METS were vague in meaning. For instance, I wondered, what measurement is the Fitbit tracker using for total distance? I found the metadata for the FitBase files, the Data Dictionary, on the FitBase website. This metadata has explanations on what each column value means. Turns out, the total distance is measured in kilometers.

Bellabeat Device – Time

While the loaded data is a lot of files, I will only be analyzing specific health measurements for the scope of this project – in particular, those that relate to the Bellabeat device of choice. After reviewing Bellabeat’s products, I chose to focus on Time, a health and wellness tracking watch for:

  • Active time
  • Sleep time
  • Mindfulness
  • Stress resistance
  • Menstrual cycle

Additional benefits of the watch include wireless connectivity and syncing to the Bellabeat app, meditation and hydration tracking, and a wellness score calculation, according to the website.

I wanted to examine trends for Fitbit data on activity and sleep because those health measures are relevant to the Time watch. Of the datasets, I incorporated the following in my analysis:

  • d.activity
  • h.intensities
  • h.calories
  • h.steps
  • sleepDay

These datasets are in wide format and saved as CSVs in the [data] folder within the overall project folder.

For inspiration on ways to analyze and visualize Fitbit tracking data, I referred to Yash Soni’s tutorial, published on freeCodeCamp. The main difference with Soni’s data is that it is specific to one individual, whereas the sample Fitbit data contains activity data for up to 33 different users, each assigned with a unique Id. But based on the tutorial, ways I can analyze data include by day of the week, using boxplots or bar graphs, or using a correlation matrix.

In addition to tidyverse, I used the following R packages:


library(lubridate) # wrangling dates

Attaching package: ‘lubridate’

The following objects are masked from ‘package:base’:

    date, intersect, setdiff, union
library(aweek)# adding week numbers to dates
library(janitor) # cleaning dataframe column names

Attaching package: ‘janitor’

The following objects are masked from ‘package:stats’:

    chisq.test, fisher.test
library(ggcorrplot) # correlation matrix
library(scales) # visualizing percentage scales

Attaching package: ‘scales’

The following object is masked from ‘package:purrr’:

    discard

The following object is masked from ‘package:readr’:

    col_factor

Daily Activity

Process – Cleaning daily activity data

1. Adding day of week and Id2 to d.activity

Since I planned to examine trends by day of the week, I used the weekdays() function in base R to convert each date into the day of the week (ex: Monday). For easier readability of Id numbers, I reassigned each Id user to a letter or a letter pair – known as Id2.

d.activity$date <- as.Date(d.activity$ActivityDate, format="%m/%d/%Y")
#View(d.activity)

# extract week day
d.activity$Day <- weekdays(d.activity$date)

li.wkdays <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday")
li.wkends <- c("Sunday", "Saturday")
li.daysofwk <- c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")

d.activity <- d.activity %>% mutate(wkdayend = case_when(Day %in% li.wkdays ~ "weekday", Day %in% li.wkends ~ "weekend"))
d.activity$Id <- as.character(d.activity$Id)
#View(d.activity)

# Pair each Id with a unique letter Id2. LETTERS is "A":"Z". As there are 33 users and only 26 alphabet letters, add 7 more unique letter pair as Id2.
Id2 <- append(LETTERS, c("ZA", "ZB", "ZC", "ZD", "ZE", "ZF", "ZG")) # new Ids
Id.unique <- d.activity %>% select(Id) %>% distinct() # distinct Ids
Id.new <- data.frame(Id.unique, Id2)
#View(Id.new)

# left join to d.activity
d.activity <- merge(x=d.activity, y=Id.new, by="Id", all.x=TRUE)
#View(d.activity)

2. A visual overview of d.activity

I plotted a correlation matrix to visualize potential correlation relationships between data value columns. I referenced Alboukadel Kassambara’s documentation of the ggcorrplot package, which is also on GitHub.

Sedentary minutes and distance have little to no correlation with calories, as expected. For each active intensity (very, moderate/fair, light), the minutes and distance have a positive correlation, as do the total distance and tracker distance. When analyzing the data, I will focus on the active exercise and filter out the sedentary minutes and distance.

Next, I wanted to know, how consistent are users recording their daily activity and using the Fitbit tracker? The number of entries varied between users, which can be seen when plotting the date and TotalDistance for each user in the scatter plot. I isolated the top 30 users with the most data.

# corr matrix for d.activity using GGally library
d.activity_sel <- d.activity %>% select(TotalDistance: TrackerDistance, VeryActiveDistance: Calories)

mtrx.activity <- round(cor(d.activity_sel), 1) # correlation matrix
pvals.activity <- cor_pmat(d.activity_sel) # p-values matrix

corr.activity <- ggcorrplot(mtrx.activity, outline.color = "gray28", type="lower", legend.title = "correlation") +
  ggtitle("Correlation of Daily Steps, Minutes, Distance, and Calories") +
  labs(subtitle = "Fitbit daily activity data for 33 users from April 12 to May 9, 2016", caption="Source: Furberg et al. 2016. Zenodo.") +
    theme(plot.title = element_text(face="bold"),
          plot.title.position= "plot",
          plot.caption.position = "plot")
corr.activity


# ggsave("./plots/corr.activity.png" , width = 8, height = 5, dpi=200)

 
# How consistent is the data for users?
userActivityCount <- d.activity %>% group_by(Id2) %>% 
  summarize(Count = n()) %>% arrange(desc(Count))
#View(userActivityCount)

# Plotting the date and TotalDistance for each user shows some users stopped data collection before May 09, 2016, the last day.
scatter.dateDistance <- d.activity %>% ggplot(aes(x = date, y=TotalDistance)) +
  geom_point(aes(color = Calories), stat="identity") +
  ggtitle("Some Fitbit users did not record distance for the full time period") +
  labs(subtitle = "Activity data was recorded Tuesday, April 12, 2016 to Thursday, May 12, 2016", caption="Source: Furberg et al. 2016. Zenodo.") +
  scale_x_date(date_breaks="2 weeks", date_labels="%b %d") +
  guides(color = guide_legend(reverse=TRUE)) +
  facet_wrap(~Id2, nrow=5) +
  theme_minimal() +
  theme(axis.text.x= element_text(hjust=0.5, vjust=0.3),
        axis.title.x = element_text(vjust = -0.5),
        plot.title = element_text(face="bold"), 
        plot.caption.position= "plot")
 
scatter.dateDistance

# ggsave("./plots/scatter.dateDistance.png" , width = 9, height = 5, dpi=300)


# Determine top 30 users with most daily data available
userActivity30 <- userActivityCount %>% top_n(30, Count) %>% glimpse()
Rows: 30
Columns: 2
$ Id2   <chr> "A", "B", "D", "E", "F", "G", "H", "J", "M", "O", "P", "Q…
$ Count <int> 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 3…
#(userActivity30)

Share – Daily activity by day of the week

The violin plots show a broader distribution in steps and distance on the weekends than weekdays, and Tuesdays have a greater median of steps and total distance.


top30.activity <- d.activity %>% filter(Id2 %in% userActivity30$Id2)
#View(top30.activity)

count_dates <- top30.activity %>% group_by(date) %>% count() %>% glimpse()
Rows: 31
Columns: 2
Groups: date [31]
$ date <date> 2016-04-12, 2016-04-13, 2016-04-14, 2016-04-15, 2016-04-1…
$ n    <int> 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30…
# isolate first two full weeks (starting from Sunday)
top30.activity.fullwks <- top30.activity %>% filter(between(date, as.Date("2016-04-17"), as.Date("2016-04-30")))

check1 <- top30.activity.fullwks %>% group_by(Id2) %>% count()
#View(check1)

#View(top30.activity.fullwks)
# steps by day
vio.stepsDay <- top30.activity.fullwks %>% ggplot(aes(x=Day, y=TotalSteps)) +
  geom_violin(aes(fill= wkdayend), alpha=0.5, width=0.8) +
  geom_boxplot(width =0.3, aes(fill=wkdayend), alpha=0.4) +
  ggtitle("Wider distribution of total steps on weekends than weekdays ", subtitle="Greater median steps on Tuesdays, of the weekdays") +
  scale_x_discrete(limits = li.daysofwk) +
  scale_y_continuous(breaks = seq(0, 28000, 7000)) +
  labs(x ="day", y = "steps", fill="day type", caption = "Fitbit data of 30 users for two weeks (Sun, April 17 to Sat, April 30, 2016) \nSource: Furberg et al. 2016. Zenodo.") +
  theme_minimal() +
  theme(plot.caption.position = "plot",
        plot.title= element_text(face="bold"))
vio.stepsDay


# ggsave("./plots/vio.stepsDay.png" , width = 8, height = 5, dpi=300)


# distance by day
vio.distanceDay <- top30.activity %>% ggplot(aes(x=Day, y=TotalDistance)) +
  geom_violin(aes(fill= wkdayend), alpha=0.5, width=0.8) +
  geom_boxplot(width =0.3, aes(fill=wkdayend), alpha=0.4) +
  ggtitle("Wider distribution of total distance on weekends than weekdays", subtitle="Of the weekdays, Tuesdays have slightly greater median distance") +
  scale_x_discrete(limits = li.daysofwk) +
  scale_y_continuous(breaks = seq(0, 28, 4)) +
  labs(x = "day", y="total distance, km", fill="day type", caption = "Fitbit data of 30 users for two weeks (Sun, April 17 to Sat, April 30, 2016) \nSource: Furberg et al. 2016. Zenodo.") +
  theme_minimal() +
  theme(plot.caption.position="plot",
        plot.title= element_text(face="bold"))
vio.distanceDay


# ggsave("./plots/vio.distanceDay.png" , width = 8, height = 5, dpi=300)

Share – Weekly Activity Target Times

In the bar graph, over three quarters of users achieved the weekly goal of 150+ minutes of light-to-moderate exercise per week for all three weeks. Around 30% of users exercised more than 75+ minutes per week for all three weeks, while about 30% of users had 0 weeks.

Based on this sample, most people meet the weekly exercise recommendations for light-to-moderate exercise, while very active exercise is more variable among users.

# visualize percentage results in bar grpah, facet wrap by activity type
bar.wkActivity <- numUsers.wkActivity %>% 
  ggplot(aes(x = wk_count, y = pcts_users)) +
  geom_bar(aes(fill=activity), stat="identity") + 
  geom_text(aes(label = count_users), vjust=1.3) +
  ggtitle("In three weeks: most users met low-moderate activity goals, \n about a third met very active goals") +
  scale_y_continuous(labels=scales::label_percent(accuracy=1)) +
  scale_fill_manual(values = c("palegreen3", "sienna2"), labels= c("low or moderate (>=150 min)", "very (>=75 min)")) +
  labs(subtitle = "Fitbit minute intensity data for three weeks (Sun, April 17 to Sat, May 07, 2016)", x = "weeks", y = "users (32 total)", caption = "Labels on bars are number of users \nSource: Furberg et al. 2016. Zenodo.", fill = "weekly activity goals") +
  theme_minimal() +
  theme(plot.caption.position="plot",
        plot.title= element_text(face="bold")) +
  facet_wrap(~activity) +
  coord_cartesian(ylim = c(0, 1))
  
bar.wkActivity


# ggsave("./plots/bar.wkActivity.png" , width = 8, height = 5, dpi=300)

Hourly Activity Data

Prepare – Merge hourly activity datasets

1. Merging and filtering steps, calories, intensity [sci] data

Looking at the other data sets beyond daily activity, I noticed I could combine the data in hourly calories, hourly steps, and hourly intensity into one data frame. I then filtered the data to only hours when people are regularly moving and exercising, between 6 AM and 9 PM, and again, for dates between a three week period, Sunday, April 17, 2016 and Saturday, May 07, 2016.

#View(h.calories)
#View(h.steps)
#View(h.intensities)

fn.countId <- function(dt) {
  dt <- dt %>% count(Id)
  return(glimpse(dt))
}

fn.countId(h.calories) %>% glimpse() #33
Rows: 33
Columns: 2
$ Id <dbl> 1503960366, 1624580081, 1644430081, 1844505072, 1927972279, …
$ n  <int> 717, 736, 708, 731, 736, 736, 736, 735, 414, 736, 472, 696, …
Rows: 33
Columns: 2
$ Id <dbl> 1503960366, 1624580081, 1644430081, 1844505072, 1927972279, …
$ n  <int> 717, 736, 708, 731, 736, 736, 736, 735, 414, 736, 472, 696, …
#View(sc)
# steps + calories
sc <- merge(h.steps, h.calories, by=c("Id","ActivityHour"))
# steps + calories + intensities
sci <- merge(sc, h.intensities, by=c("Id","ActivityHour"))
sci <- merge(sci, Id.new, by="Id", all.x=TRUE) # add Id2
#View(sci)


# set timezone
Sys.setenv(tz="America/New_York")
Sys.timezone()
[1] "America/New_York"
# convert ActivityHour to datetime format
sci$datetime <-strptime(sci$ActivityHour, "%m/%d/%Y %I:%M:%S %p")

# extract hour of day from time, isolate hour
fn.extractHr <- function(dt){
  dt$date <- as.Date(dt$datetime, format="%m/%d/%Y")
  dt$time <- as.POSIXct(dt$datetime, format="%H:%M %p")
  dt$hour <- format(dt$time, format="%I %p")
  dt$hour_min <- format(dt$time, format="%I:%M %p")
  return(dt)
}


sci <- fn.extractHr(sci)
#View(sci)
sci$Day <- weekdays(sci$date)

# range of hours when most people are moving (not sleeping)
amHrs <- paste(6:9, "AM", sep=" ")
amHrs <- paste0(0, amHrs)
midHrs <- paste(10:11, "AM", sep=" ")
pmHrs <- paste(1:9, "PM", sep=" ")
pmHrs <- paste0(0, pmHrs)
movingHrs <- c(amHrs, midHrs, "12 PM", pmHrs)
#glimpse(movingHrs)

# filter sci data for select hours and filter for dates between 
sci_fil <- sci %>% filter(hour %in% movingHrs) %>% filter(between(date, as.Date("2016-04-17"), as.Date("2016-05-07")))
#View(sci_fil)


# examine Id2 in sci_fil - Does it match users in userTypes? YES
Id.sci_fil <- sci_fil %>% group_by(Id2) %>% summarize(count = n()) # 32 users
#View(Id.sci_fil)
#View(userTypes)

testIdmatch <- merge(x=Id.sci_fil, y = userTypes, by="Id2", all.x = TRUE )
#View(testIdmatch)

# merge sci with userTypes
sci_fil <- merge(x=sci_fil, y=userTypes, by="Id2", all.x = TRUE )

Analyze – Group data by hour and day

2. What hours of each day are people exercising?

After prepping the combined hourly steps, calories, and intensity [sci_fil] for analysis, I analyzed activity trends by hour of day to see when users were exercising the most and found mean averages for each measurement.

#View(sci_fil)

fn.hourDay.avgs <- function(dt) {
  dt <- dt %>% group_by(hour, Day) %>% 
    summarize(avgStepTotal = mean(StepTotal), avgCalories = mean(Calories),
            avgTotalIntensity = mean(TotalIntensity)) %>%
  mutate(across(where(is.numeric), round))
  return(dt)
}
  
sciAvgs.DayHour <- fn.hourDay.avgs(sci_fil)
`summarise()` has grouped output by 'hour'. You can override using the `.groups` argument.
#View(sciAvgs.DayHour)

Share – Intensity heatmaps by hour and day of week

I plotted the hourly activity data [sci], using different heatmaps. When making the heatmaps, I referenced Yan Holtz’s the R Graph Gallery’s heatmap tutorial, as well as the post on RColorBrewer’s palettes, which was helpful in selecting the colors for the heatmaps.


# Overall average total intensities by hour and day of the week
heat.DayHour.intensity <- sciAvgs.DayHour %>% ggplot(aes(x = hour, y=Day, fill=avgTotalIntensity)) +
  geom_tile() +
  ggtitle("On Average, More Intense Activity on Wednesdays after 5 p.m., Saturday 1 p.m., Tuesdays at Noon") +
  labs(fill = "average intensity", subtitle="More intense workouts after 5 p.m. in earlier weekdays of the week (Mon.- Wed.)", caption = "Fitbit data for three weeks (Sun, April 17 to Sat, May 07, 2016) \nSource: Furberg et al. 2016. Zenodo.", y="day") +
  scale_x_discrete(limits= movingHrs) +
  scale_y_discrete(limits= rev(li.daysofwk)) +
  scale_fill_distiller(palette="Oranges", direction =1) +
  theme_minimal() +
  theme(plot.caption.position = "plot",
        plot.title.position = "plot",
        plot.title= element_text(face="bold"))

heat.DayHour.intensity


# ggsave("./plots/heat.DayHour.intensity.png" , width = 10.5 , height = 7, dpi=300)

# Overall average total steps by hour and day of the week
heat.DayHour.steps <- sciAvgs.DayHour %>% ggplot(aes(x = hour, y=Day, fill=avgStepTotal)) +
  geom_tile() +
  ggtitle("On Average, More Steps on Wednesdays 5-6 p.m., Saturday 1-2 p.m., and Tuesdays at Noon") +
  labs(fill = "average steps", subtitle="More intense workouts after 5 p.m. on earlier weekdays of the week (Mon.-Wed.)", caption = "Fitbit data for three weeks (Sun, April 17 to Sat, May 07, 2016) \nSource: Furberg et al. 2016. Zenodo.", y="day") +
  scale_x_discrete(limits= movingHrs) +
  scale_y_discrete(limits= rev(li.daysofwk)) +
  scale_fill_distiller(palette="GnBu", direction =1) +
  theme_minimal() +
  theme(plot.caption.position = "plot",
        plot.title.position="plot",
        plot.title= element_text(face="bold"))
heat.DayHour.steps


# ggsave("./plots/heat.DayHour.steps.png" , width = 10.5 , height = 7, dpi=300)

# separate data by userType 
avgsByHours_LM <- sci_fil %>% filter(user_type == "LM") %>% fn.hourDay.avgs()
`summarise()` has grouped output by 'hour'. You can override using the `.groups` argument.
#View(avgsByHours_LM)

avgsByHours_LMV <- sci_fil %>% filter(user_type == "LMV") %>% fn.hourDay.avgs()
`summarise()` has grouped output by 'hour'. You can override using the `.groups` argument.
#View(avgsByHours_LMV)


# for frequent light-moderate exercisers only - average total intensities by hour and day of week
heat.LM.intensity <- avgsByHours_LM %>% 
  ggplot(aes(x = hour, y = Day, fill=avgTotalIntensity)) +
  geom_tile() +
  ggtitle("On Average for Light/Moderate Exercisers: More Intense Activity on Weekends, and \nWeekdays After 6 p.m.") +
  labs(fill = "average intensity", subtitle="Monday, Tuesday, and Wednesday show more intense activity, of the weekdays", caption = "Fitbit data for three weeks (Sun, April 17 to Sat, May 07, 2016) \nSource: Furberg et al. 2016. Zenodo.", y="day") +
  scale_y_discrete(limits= rev(li.daysofwk)) +
  scale_x_discrete(limits=movingHrs) +
  scale_fill_distiller(palette="YlGn", direction =1) +
  theme_minimal() +
  theme(plot.caption.position = "plot",
        plot.title.position = "plot",
        plot.title= element_text(face="bold"))
heat.LM.intensity


# ggsave("./plots/heat.LM.intensity.png", width = 10.5 , height = 7, dpi=300)

# frequent light-moderate-very active exercisers - average total intensities by hour and day of week
heat.LMV.intensity <- avgsByHours_LMV %>% 
  ggplot(aes(x = hour, y = Day, fill=avgTotalIntensity)) +
  geom_tile() +
  ggtitle("On Average for Very Active Exercisers: More Activity After 5 p.m., Monday through Wednesday") +
  labs(fill = "average intensity", subtitle="Monday, Tuesday, and Saturday hours around noon and Sunday nights also have more intense activity", caption = "Fitbit data for three weeks (Sun, April 17 to Sat, May 07, 2016) \nSource: Furberg et al. 2016. Zenodo.", y="day") +
  scale_y_discrete(limits= rev(li.daysofwk)) +
  scale_x_discrete(limits=movingHrs) +
  scale_fill_distiller(palette="OrRd", direction =1) +
  theme_minimal() +
  theme(plot.caption.position = "plot",
        plot.title.position = "plot",
        plot.title = element_text(face="bold"))
heat.LMV.intensity


# ggsave("./plots/heat.LMV.intensity.png" , width = 10.5 , height = 7, dpi=300)

Key Findings – Hourly Activity

Based on the Fitbit hourly exercise heatmaps, I would suggest the following to improve Bellabeat Time:

  • Provide a questionnaire for users in the tracking app to ask what kind of exercise intensity they intend on doing on a weekly basis (very, moderate, or light activity) and suggest optimal exercise hours based on hourly trends of Fitbit users, grouped by exercise intensity type.

  • As weekdays after 5 p.m. and weekend afternoons are on average more popular intense activity workout times, giving users the option to create an hourly exercise schedule, including setting reminders to exercise at certain times of the day could improve consistency of workouts.

Sleep Tracking

Prepare – Clean sleep tracking data

1. Cleaning the sleep data The original sleepDay only has data for 24 users. This small sample size, being < 30, is a limitation of the data analysis.

First, I wanted to see how consistently each user logged their sleep minutes over a period of time. Again, I performed the usual preparation steps, assigning Id2 to each Id for easier readability and joining the daily activity data with the sleep data.

When cleaning the data, I found three users with duplicate data for the same dates. I removed these duplicate rows before plotting the data.


# merge sleepDay and d.activity
#View(sleepDay)
#View(d.activity)
sleepDay$date <- as.Date(sleepDay$SleepDay, format="%m/%d/%Y")
sleepDay <- merge(x=sleepDay, y=Id.new, by="Id", all.x = TRUE )
#glimpse(sleepDay) # 413 rows

sleepDay_sel <- sleepDay %>% select(TotalSleepRecords: Id2) 

sleepId <- sleepDay_sel %>% group_by(Id2) %>% summarize(n()) %>% glimpse()
Rows: 24
Columns: 2
$ Id2   <chr> "A", "C", "D", "E", "G", "H", "I", "L", "M", "O", "P", "Q…
$ `n()` <int> 25, 4, 3, 5, 28, 1, 15, 28, 8, 26, 24, 28, 5, 28, 31, 26,…
#View(sleepId)

sleepDates <- sleepDay_sel %>% group_by(date) %>% summarize(n()) %>% glimpse()
Rows: 31
Columns: 2
$ date  <date> 2016-04-12, 2016-04-13, 2016-04-14, 2016-04-15, 2016-04-…
$ `n()` <int> 13, 14, 13, 17, 14, 12, 10, 14, 15, 15, 13, 15, 13, 13, 1…
#View(sleepDates)

# check sleep data for count by date and user Id2
sleepTest <- sleepDay_sel  %>% group_by(date, Id2) %>% summarize(count = n()) %>% arrange(desc(count))
`summarise()` has grouped output by 'date'. You can override using the `.groups` argument.
#View(sleepTest)

## Id2: P, S, ZD each have 2 entries for same date (duplicate data)
sleepTest2 <- sleepDay_sel %>% filter( Id2 == "ZD" & date == as.Date("2016-04-25") | Id2 == "P" & date == as.Date("2016-05-05") |
                                          Id2 == "S" & date== as.Date("2016-05-07"))
# View(sleepTest2) 

# remove duplicated rows in sleep data
sleepDay_sel <- sleepDay_sel %>% distinct() %>% glimpse() # 410 rows
Rows: 410
Columns: 5
$ TotalSleepRecords  <dbl> 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
$ TotalMinutesAsleep <dbl> 327, 384, 412, 340, 700, 304, 360, 325, 361,…
$ TotalTimeInBed     <dbl> 346, 407, 442, 367, 712, 320, 377, 364, 384,…
$ date               <date> 2016-04-12, 2016-04-13, 2016-04-15, 2016-04…
$ Id2                <chr> "A", "A", "A", "A", "A", "A", "A", "A", "A",…
# merge sleep and activity data
sleepActivity <- merge(x=sleepDay_sel, y=d.activity, by=c("Id2", "date"), all.x=TRUE)

# add columns for total minutes of exercise
sleepActivity <- sleepActivity %>% mutate(LightModMinutes = (LightlyActiveMinutes + FairlyActiveMinutes)) %>% 
  mutate(TotalActiveMinutes = (VeryActiveMinutes + LightModMinutes))

#View(sleepActivity)
# add exercise intensity type of each user
sleepActivity.userTypes <- merge(x = sleepActivity, y = userTypes, by = "Id2", all.x=TRUE)

#(sleepActivity.userTypes)

Daily Sleep Records

Analyze – Consistency of sleep records per user

Again, I filtered the daily sleep data to three weeks. I wanted to see how consistent users were recording their sleep time, so I counted the total number of date entries for each user.

# filter data for three week period
sleepActivity.userTypes_fil <- sleepActivity.userTypes %>% filter(between(date, as.Date("2016-04-17"), as.Date("2016-05-07")))

#View(sleepActivity.userTypes_fil)
# How consistent is the data?
## How many days each user logged sleep data?
sleepUsers <- sleepActivity.userTypes_fil %>% group_by(Id2) %>% summarize(num_days=n()) %>% glimpse() # 23 users
Rows: 23
Columns: 2
$ Id2      <chr> "A", "C", "D", "E", "G", "H", "I", "L", "M", "O", "P",…
$ num_days <int> 17, 3, 2, 2, 18, 1, 12, 21, 4, 18, 17, 18, 4, 18, 21, …
#View(sleepUsers)

## count how many users logged how many days
sleep.dateCount <- sleepUsers %>% ungroup() %>% group_by(num_days) %>% summarize(user_count=n())

userCount <- sleep.dateCount %>% summarize(totalUsers = sum(user_count))
#glimpse(userCount)

Share – Visualize user percentage by number of days

The bar graph shows the proportion of 23 users that logged their sleep data for how many days of the selected three weeks. Only 4 users (around 18%) logged sleep minutes for all 21 days.

# plot to show percentage of users and record of sleep data over time

bar.sleep.dates <- sleep.dateCount %>% ggplot(aes(x=num_days, y = user_count/ sum(user_count))) + 
  geom_bar(stat="identity", fill = "slategray2") +
  geom_text(aes(label = user_count), vjust=-.25) +
  ggtitle("Fewer than 20% of users recorded sleep data daily for 3 weeks") +
  labs( x = "days", y = "users (23 total)", subtitle="Fitbit sleep tracking data for April 17 through May 7, 2016", caption="Bar labels are number of users \nSource: Furberg et al. 2016. Zenodo.") +
  scale_x_continuous(breaks=seq(1, 21, 2)) +
  scale_y_continuous(labels=scales::label_percent(accuracy=1)) +
  coord_cartesian(ylim=c(0, 1)) +
  theme_minimal() +
  theme(plot.title = element_text(face="bold"))

bar.sleep.dates


# ggsave("./plots/bar.sleep.dates.png" , width = 9 , height = 7, dpi=300)

Bar graph references:

  • Thomas Neitmann’s blogpost about percentage scales was a helpful explainer on how to use the scales package. Note that percent() and percent_format() are retired, according to the scales package documentation, so I used label_percent() instead.

  • To adjust the y-axis scale limits to show 0 to 100%, I used coord_cartesian(). To read more about this, see roelpi’s blog post about adding percentage limits using ggplot2

  • I referred to a Stack Overflow discussion for adding labels to bars in ggplot2, namely the response from user rcs to adjust the placement using vjust.

Key Findings – Sleep Records

The lack of consistent daily sleep logging among Fitbit users shows a problematic trend. Perhaps users are not wearing their tracking watches while they sleep. * I suggest that the Bellabeat team create a wearable sleep tracking ring, which some companies like Oura have designed. This could act as a companion to the Time watch, and the smaller device might encourage more people to wear a tracker when sleeping. * Providing daily reminders to wear the watch while sleeping, through the Bellabeat app, could help promote consistency of sleep tracking data collection.

Healthy Daily Sleep

Analyze – Target sleep day percentages VS. user percentages

2. How often do Fitbit users get enough daily sleep?

According to a 2015 study, Watson et al, cited by the CDC, the healthy amount of sleep for an adult is 7 hours or more a night. Since the sleep data is in minutes, this converts to 420+ minutes a night.

I categorized each day as >= 420 min. or < 420 min. to determine each user’s percentage of total days of sleep records. By grouping the data further, I found the total count of users and the percentage of healthy days of the sleep days recorded per user.

Next, I grouped the percentage of healthy sleep data into percent ranges by quarters and recounted the total users per percent range and the respective user percentages. This data is easier to visualize than individual non-grouped healthy sleep day percentages.


# For each user, how often did they get a healthy amount of sleep? 
#View(sleepActivity.userTypes_fil)

healthy.sleepDays <- sleepActivity.userTypes_fil %>%
  mutate(sleepTarget = case_when(TotalMinutesAsleep >= 420 ~ "healthy",
                                 TotalMinutesAsleep < 420 ~ "no"))

## percentages by number of days they recorded sleep data
healthy.sleepDays.byUser <- healthy.sleepDays %>% group_by(Id2) %>% 
  summarize(healthy_sleep_days = sum(sleepTarget == "healthy"),
            total_days = n()) %>%
  mutate(pct_healthy_sleep = round(healthy_sleep_days / total_days, digits=2))

#View(healthy.sleepDays.byUser)

## group by percentages of healthy sleep days to find user counts and percentages
healthy.sleepDays.pctUsers <- healthy.sleepDays.byUser %>% group_by(pct_healthy_sleep) %>% 
  summarize(count_user = n()) %>% 
  mutate(pct_user = round( count_user / sum(count_user), digits=2)) %>% ungroup()
glimpse(healthy.sleepDays.pctUsers)
Rows: 16
Columns: 3
$ pct_healthy_sleep <dbl> 0.00, 0.12, 0.25, 0.28, 0.33, 0.45, 0.52, 0.5…
$ count_user        <int> 5, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2
$ pct_user          <dbl> 0.22, 0.04, 0.04, 0.04, 0.09, 0.04, 0.04, 0.0…
sum(healthy.sleepDays.pctUsers$count_user) #23 total users
[1] 23
ranges.healthySleepDays.pctUsers <- healthy.sleepDays.pctUsers %>% 
  mutate( ranges_sleepDays = dplyr::case_when( pct_healthy_sleep >= 0.75 ~ "75-100%",
                    pct_healthy_sleep >= 0.50 ~ "50-74%",
                     pct_healthy_sleep >= 0.25 ~ "25-49%",
                    pct_healthy_sleep > 0 ~ "0-24%"))

# replace NA with 0-24%
ranges.healthySleepDays.pctUsers[is.na(ranges.healthySleepDays.pctUsers)] = c("0-24%")       

ranges.healthySleepDays.pctUsers <- ranges.healthySleepDays.pctUsers %>% 
  select(!pct_user) %>% 
  group_by(ranges_sleepDays) %>% 
  summarize(count_user = sum(count_user)) %>% ungroup() %>% 
  mutate(pct_user = round( count_user / sum(count_user), digits=2)) %>% ungroup()
  
#View(ranges.healthySleepDays.pctUsers)
            

Share – Visualize target sleep day percentages by users

In the bar chart, fewer than 25% of Fitbit users (5 users) recorded at least 75% days of healthy sleep duration (at least 7 hours a day), while fewer than 25% of users (6 users) had fewer than a quarter days of enough sleep. This suggests that users could use more consistency in the amount of sleep they are getting.


bar.pctHealthySleepDays <- ranges.healthySleepDays.pctUsers %>% 
  ggplot(aes(x = ranges_sleepDays, y = pct_user)) +
  geom_bar(stat="identity", fill="lightskyblue") +
  geom_text(aes(label = count_user), vjust = 1.4) +
  ggtitle("Fewer than 1/4 Fitbit users had at least \n75% of healthy sleep days (7+ hours/day)") +
  labs(x = "days of healthy sleep", y="users (23 total)", subtitle="Over 1/4 users had fewer than 1/4 total healthy sleep days (April 17 to May 07, 2016)", caption = "Bar labels are number of users \nSource: Furberg et al. 2016. Zenodo.") +
  scale_y_continuous(labels=scales::label_percent(accuracy=1)) +
  scale_x_discrete() +
  theme_minimal() +
  theme(plot.caption.position = "plot",
        plot.title= element_text(face="bold")) +
  coord_cartesian(ylim = c(0, 1))
bar.pctHealthySleepDays 


# ggsave("./plots/bar.pctHealthySleepDays.png" , width = 10, height = 7, dpi=300)

Key Findings – Suggestions for More Healthy Sleep Days

As nearly half of the Fitbit users had fewer than 50% healthy sleep days of their total sleep records, another sleep data trend is that most people are not sleeping the healthy recommended amount of 7+ hours per day.

I suggest that Bellabeat create a function in the app for a user to select target sleep and waking times that reflect a healthy amount of daily sleep. The addition of notification reminders for users to sleep at a certain time could help increase the number of users who are getting enough daily sleep.

Sleep VS. Exercise Activity

Analyze

If a person, on average, sleeps enough in a week, does that correlate with them spending more time exercising during that same week?

I wanted to examine:

  • Are sleep times and activity times correlated?
  • Are sleep times and calories correlated?

Taking the merged sleep and daily activity dataset, I grouped the data by user and week, and found the average values for total sleep for each week and the total weekly activity minutes, steps, calories, as well as the mean weekly calories.

# Is there a correlation of healthy daily sleep and exercise activity?
wk.sleepActivity <- sleepActivity.userTypes_fil %>% ungroup() %>% group_by(Id2, week_date) %>% 
  summarize(avgSleepMin = mean(TotalMinutesAsleep),
            medianSleepMin = median(TotalMinutesAsleep),
            wkActiveMin = sum(TotalActiveMinutes),
            wkLightModMin = sum(LightModMinutes),
            wkVeryActiveMin = sum(VeryActiveMinutes),
            totalSteps = sum(TotalSteps),
            totalCalories = sum(Calories),
            avgCalories = mean(Calories))
`summarise()` has grouped output by 'Id2'. You can override using the `.groups` argument.
wk.sleepActivity <- merge(x = wk.sleepActivity, y = userTypes, by="Id2", all.x= TRUE)

# View(wk.sleepActivity)

Share

For the first scatter plot showing average sleep time and total weekly activity time, I colored the points by type of exerciser, using the user intensity types from the earlier activity analysis. The size of the points reflects the total weekly calories. Both groups of exercisers show a slightly positive correlation of total weekly exercise time and weekly average sleep time.

In the second scatter plot, total weekly calories is also slightly positively correlated with weekly average sleep time, though there are outlier cases.

# weekly active minutes vs. average sleep minutes
scatter.wk.sleepActiveMin <- wk.sleepActivity %>% 
  ggplot(aes(x = avgSleepMin, y = wkActiveMin)) +
  geom_point(aes(color = user_type, size=totalCalories)) +
  geom_smooth(method="lm", se = FALSE, alpha = 0.2, aes(color = user_type)) +
  geom_vline(xintercept = 420, linetype = "dashed", color = "red") +
  ggtitle("Weekly Average Sleep and Activity Times Are Positively Correlated") +
  scale_y_continuous(breaks = seq(0, 28000, 4000)) +
  scale_color_manual(values = c("palegreen3", "sienna2", "lightgoldenrod2"), labels= c("low or moderate", "low, moderate, or very", "not active")) +
  labs(subtitle = "Weekly Fitbit data for April 17 to May 7, 2016", x = "average sleep minutes", y = "total weekly active minutes", size = "total week calories", color = "type of exerciser", caption = "Dotted line is healthy sleep per day \nSource: Furberg et al. 2016. Zenodo.") +
  theme_minimal() +
  theme(plot.caption.position = "plot",
        plot.title= element_text(face="bold"))
scatter.wk.sleepActiveMin
`geom_smooth()` using formula 'y ~ x'

# ggsave("./plots/scatter.wk.sleepActiveMin.png" , width = 10, height = 7, dpi=300)

# total weekly calories vs. average sleep minutes
scatter.wk.sleepCalories <- wk.sleepActivity %>% 
  ggplot(aes(x = avgSleepMin, y = totalCalories)) +
  geom_point() +
  geom_smooth(method="lm", se = FALSE, color = "slategray2") +
  geom_vline(xintercept = 420, linetype = "dashed", color = "red") +
  ggtitle("Weekly Average Sleep Time and Total Calories Are Positively Correlated") +
  scale_y_continuous(breaks = seq(0, 28000, 4000)) +
  labs(subtitle = "Weekly Fitbit data for April 17 to May 7, 2016", x = "average sleep minutes", y = "total weekly calories", caption = "Dotted line is healthy sleep per day \nSource: Furberg et al. 2016. Zenodo.") +
  theme_minimal() +
  theme(plot.caption.position="plot",
        plot.title= element_text(face="bold"))
scatter.wk.sleepCalories
`geom_smooth()` using formula 'y ~ x'

# ggsave("./plots/scatter.wk.sleepCalories.png" , width = 10, height = 7, dpi=300)

Key Findings – Sleep and exercise activity

More sleep is positively correlated with more exercise and calorie burning, by week. If Bellabeat Time users are provided daily reminders to meet sleep targets (7+ hours / day), that could in turn, improve their exercise activity.

Summary of Project Analysis

For this case study, I analyzed Fitbit data related to activity and sleep to examine trends among fitness tracking users, to then inform insights for improving the Bellabeat Time fitness watch, which is designed to track user activity and sleep patterns, among other measures.

Activity

I analyzed daily activity data related to activity intensities (very, moderate, and lightly active), calories, and steps. By determining whether or not users achieved healthy light/moderate or very active times per week and how frequently they met those goals (a majority of weeks being at least 2 of 3 weeks), I found two main groups of exercisers:

  • Light/moderate exercisers (but not very active) – LM
  • light/moderate and very active exercisers – LMV

Then, I looked at hourly activity data, using this same grouping of exerciser types. I examined when people most frequently exercised, based on average total intensities by hour and day of the week and determined separate trends for different types of exercisers, along with trends for the overall users.

Activity Trends

  • Wider distribution of amount of activity on weekends than weekdays
  • Of the weekdays, slightly greater median of activity intensity on Tuesdays
  • More weekly calories burned in less time and shorter distance for users who frequently are very active, as compared to exercisers who are only frequently light/moderately active
  • Overall, more intense workouts occur on Tuesdays at noon, Saturdays at 1 p.m., and after 5 p.m. on weekdays
  • Frequent light/moderate exercisers tend to be more active on weekends, and weekdays after 6 p.m.
  • Frequent very active exercisers tend to be more active on Monday, Tuesday, and Saturday around noon; and Sunday through Wednesday after 5 p.m.

Sleep

For the sleep day data, I analyzed the following and found these trends:

  • How often were users recording sleep data?
    • Fewer than 20% of users consistently recorded sleep data over three weeks
  • How often were users getting a healthy amount of sleep each day (at least 7 hours), of the total days they recorded sleep data?
    • Fewer than a quarter of users had at least 75% days of healthy sleep
    • Over a quarter of users had only less than 25% days of healthy sleep
  • By merging the sleep and activity data and visualizing total weekly active minutes and average sleep minutes per week, there is a slightly positive correlation between sleep and activity for exercisers of either intensity group (LMV or LM).

Data Limitations

The data used for this analysis was limited to a small sample size (around 30 or fewer users).

In particular, the sleep dataset had information for fewer than 30 users. The sleep trends found from this data analysis are not a significant representation of a larger population, due to the small sample population and missing data.

As the data collected was for a short time period, between April and May 2016, the health activity and sleep patterns are limited to an optimal season of exercise (spring/fall, depending on the hemisphere location of each user). Activity and sleep trends could vary during other seasons of the year.

Due to the anonymity of user Ids, no information was known about users’ biological sex or age, nor work hours, living conditions, and class status, characteristics which could impact a person’s exercise and sleep schedules.

Act – High-level Recommendations for Stakeholders

The following recommendations are based on trends from the analysis and can be applied to Bellabeat’s fitness tracking watch, Time.

Activity tracking suggestions

  • Provide a questionnaire for users to determine the type of intensity workouts they plan on doing regularly and an option to set weekly exercise goals
  • Provide a weekly summary graph to show times of activity intensity per day and weekly totals per type of intensity (light/moderate and very active)
  • Include a reminder of optimal intensity exercise goals during the week and the difference of time needed to complete that weekly goal, based on exercise intensity time already spent
  • Give users the option to create hourly exercise schedules with reminder notifications
  • Suggest optimal exercise hours based on the type of exercise they intend on doing, as answered in their questionnaire

Sleep tracking suggestions

  • Create an optional sleeping tracking ring to act as a companion to the Time watch. The ring is a smaller wearable option that could encourage users to wear a tracking device daily when sleeping.
  • Provide the option of daily notifications to remind users to wear the watch while sleeping
  • Allow users to select optimal sleep and waking times and set reminders to go to bed by that certain time
  • Inform users of the benefits of healthy amounts of sleep, correlated with more weekly exercise

References

The following references are organized into sections by order of appearance.

Data, Background Information

  1. Furberg, R., Brinton, J., Keating, M., & Ortiz, A. (2016). Crowd-sourced Fitbit datasets 03.12.2016-05.12.2016. [Data set]. Zenodo. https://doi.org/10.5281/zenodo.53894. Retrieved from https://www.kaggle.com/arashnic/fitbit.
  2. Fitabase. (2018). Fitabase Data Dictionary. Retrieved from https://www.fitabase.com/media/1930/fitabasedatadictionary102320.pdf
  3. National Health Interview Survey (NHIS), Centers for Disease Control and Prevention (CDC) / National Center for Health Statistics (NCHS). (2022). Physical Activity. Retrieved from https://www.healthypeople.gov/2020/topics-objectives/topic/physical-activity/national-snapshot.
  4. CDC. (2022) Physical Activity. Retrieved from https://www.cdc.gov/physicalactivity/basics/adults/index.htm.
  5. Watson NF, Badr MS, Belenky G, et al. Recommended amount of sleep for a healthy adult: a joint consensus statement of the American Academy of Sleep Medicine and Sleep Research Society. Sleep. 2015;38(6):843–844. http://dx.doi.org/10.5665/sleep.4716.

Coding Resources

  1. Soni, Yash. (2018) How I analyzed the data from my Fitbit to improve my overall health. freeCodeCamp. https://www.freecodecamp.org/news/how-i-analyzed-the-data-from-my-fitbit-to-improve-my-overall-health-a2e36426d8f9/.
  2. Kassambara, Alboukadel. ggcorrplot: Visualization of a correlation matrix using ggplot2 (0.1.3). https://rpkgs.datanovia.com/ggcorrplot/.
  3. Holtz, Yan. (2018) Violin plot with included boxplot and sample size in ggplot2. The R Graph Gallery. https://www.r-graph-gallery.com/violin_and_boxplot_ggplot2.html.
  4. Kamvar, Zhian N. (2021) Package aweek: Convert Dates to Arbitrary Week Definitions(1.02). Retrieved from https://cran.r-project.org/web/packages/aweek/aweek.pdf.
  5. Li, Deanna. (2020) Basic R Guide for NSC Statistics, Chapter 19: Scatterplots and Best Fit Lines - Two Sets. (Bookdown) https://bookdown.org/dli/rguide/scatterplots-and-best-fit-lines-two-sets.html.
  6. Holtz, Yan. (2018). ggplot2 heatmap. The R Graph Gallery. https://www.r-graph-gallery.com/79-levelplot-with-ggplot2.html.
  7. Holtz, Yan. (2018). R Color Brewer’s Palettes. https://www.r-graph-gallery.com/38-rcolorbrewers-palettes.html.
  8. Neitmann, Thomas. (2020) Transform a ggplot2 Axis to a Percentage Scale. https://thomasadventure.blog/posts/ggplot2-percentage-scale/.
  9. Wickham, Hadley and Seidel, Dana. (2020) Package scales: Scale Functions for Visualization(1.1.1). https://cran.r-project.org/web/packages/scales/scales.pdf.
  10. roelpeters.be. (2019) Add percentages to your axes in R’s ggplot2 (and set the limits). https://www.roelpeters.be/add-percentages-to-your-axes-in-rs-ggplot2-and-set-the-limits/.
  11. rcs. (2012) How to put labels over geom_bar for each bar in R with ggplot2. Stack Overflow. https://stackoverflow.com/questions/12018499/how-to-put-labels-over-geom-bar-for-each-bar-in-r-with-ggplot2.
LS0tCnRpdGxlOiAiQ2FzZSBTdHVkeSAtIEZpdG5lc3MgYW5kIEhlYWx0aCBUcmFja2luZyBBbmFseXNpcyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICBodG1sX25vdGVib29rOiBkZWZhdWx0CmF1dGhvcjogIkp1bGlhIEhvbmciCmRhdGU6IE1hcmNoIDI5LCAyMDIyCi0tLQoKIyMjIEludHJvCgojIyMjIFByb2plY3Qgb3ZlcnZpZXcKSW4gdGhpcyBjYXNlIHN0dWR5LCBJIGFtIGEganVuaW9yIGRhdGEgYW5hbHlzdCB3aG8gaXMgd29ya2luZyBmb3IgdGhlIG1hcmtldGluZyBhbmFseXRpY3MgdGVhbSBhdCBCZWxsYWJlYXQsIGEgaGlnaC10ZWNoIGNvbXBhbnkgdGhhdCBkZXNpZ25zIGhlYWx0aCB0cmFja2luZyBwcm9kdWN0cyBmb3Igd29tZW4uIFRoaXMgaHlwb3RoZXRpY2FsIHNjZW5hcmlvIGlzIHByb3ZpZGVkIGJ5IEdvb2dsZSdzIERhdGEgQW5hbHl0aWNzIENlcnRpZmljYXRlIFByb2dyYW0gdGhyb3VnaCBDb3Vyc2VyYSwgYW5kIEkgd2lsbCBiZSBvdXRsaW5pbmcgdGhlIHN0YW5kYXJkIGRhdGEgYW5hbHlzaXMgcGF0aHdheSB0aHJvdWdob3V0IHRoaXMgcHJvamVjdCAoYXNrLCBwcmVwYXJlLCBwcm9jZXNzLCBhbmFseXplLCBzaGFyZSwgYW5kIGFjdCkuCgoKIyMjIEFzawoKIyMjIyBCdXNpbmVzcyB0YXNrCk15IG1haW4gb2JqZWN0aXZlIGZvciB0aGlzIHByb2plY3QgaXMgdGhlIGZvbGxvd2luZzoKQmFzZWQgb24gdHJlbmRzIGluIG5vbi1CZWxsYWJlYXQgc21hcnQgZGV2aWNlcyBmb3IgdHJhY2tpbmcgaGVhbHRoIGRhdGEsIHdoYXQgdHJlbmRzIG9jY3VyIGFtb25nIGNvbnN1bWVycyBhbmQgaG93IGNhbiB0aGV5IGJlIGFwcGxpZWQgdG8gYSBCZWxsYWJlYXQgZGV2aWNlIGFuZCB0aGUgY29tcGFueSdzIG1hcmtldGluZyBzdHJhdGVneT8KCiMjIyMgU3Rha2Vob2xkZXJzCkFmdGVyIG15IGFuYWx5c2lzLCBJIHdvdWxkIHNoYXJlIG15IGhpZ2gtbGV2ZWwgZmluZGluZ3MgYW5kIHByb3ZpZGUgc3VnZ2VzdGlvbnMgdG8gaW1wcm92ZSBhIEJlbGxhYmVhdCBkZXZpY2UgdG8gdGhlIG1haW4gc3Rha2Vob2xkZXIsIFVyxaFrYSBTcsWhZW4sIG9uZSBvZiB0aGUgdHdvIGZvdW5kZXJzIG9mIEJlbGxhYmVhdC4gSSB3b3VsZCBhbHNvIHNoYXJlIG15IGZpbmRpbmdzIGFuZCBtb3JlIGRldGFpbGVkIGFuYWx5c2VzIGFuZCBjb2RlIHdpdGggdGhlIGRhdGEgYW5hbHl0aWNzIHRlYW0gcHJpb3IgdG8gc2hhcmluZyBoaWdoLWxldmVsIHJlc3VsdHMgd2l0aCB0aGUgZm91bmRlci4KCiMjIyBQcmVwYXJlCgojIyMjIE5vbi1CZWxsYWJlYXQgRGF0YQpUaGUgZGF0YSBmb3Igbm9uLUJlbGxhYmVhdCBkZXZpY2VzIHdhcyBkb3dubG9hZGVkIGZyb20gS2FnZ2xlIHVzZXIgTcO2Yml1czogW0ZpdGJpdCBGaXRuZXNzIFRyYWNrZXIgRGF0YV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9hcmFzaG5pYy9maXRiaXQpLiBUaGlzIGRhdGEgY29sbGVjdGlvbiBjb250YWlucyAxOCBDU1YgZmlsZXMgYWJvdXQgZXhlcmNpc2UgYWN0aXZpdHksIGludGVuc2l0eSwgY2Fsb3JpZXMsIHN0ZXBzLCBNRVRTIChtZXRhYm9saWMgZXF1aXZhbGVudCBvZiB0YXNrKSwgaGVhcnRyYXRlLCB3ZWlnaHQsIGFuZCBzbGVlcCBhY3Jvc3MgZGlmZmVyZW50IHRpbWUgbWVhc3VyZW1lbnRzIChkYXlzLCBob3VycywgbWludXRlcywgc2Vjb25kcykuCgpUaGUgb3JpZ2luYWwgZGF0YXNldHMgd2VyZSBjcmVhdGVkIGJ5IEZ1cmJlcmcsIFIuIGV0IGFsLCBwdWJsaXNoZWQgb24gW1plbm9kb10oaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZC81Mzg5NCMuWWpRSFhpLUIybEspIGluIE1heSAyMDE2LiBJdCBpcyBvcGVuLXNvdXJjZWQgd2l0aCBhIFtDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uIDQuMCBJbnRlcm5hdGlvbmFsIFB1YmxpYyBMaWNlbnNlXShodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkvNC4wL2xlZ2FsY29kZSkuClRoZSBkYXRhIGlzIGJhc2VkIG9uIGEgMjAxNiBjcm93ZHNvdXJjZWQgc3VydmV5IG9mIEZpdGJpdCB1c2VycywgY29uZHVjdGVkIGJ5IEFtYXpvbiBNZWNoYW5pY2FsIFR1cmsuCgoKYGBge3IgdGlkeXZlcnNlIHBhY2thZ2V9CmxpYnJhcnkodGlkeXZlcnNlKQoKYGBgCgoKIyMjIyBMb2FkIGRhdGFzZXRzCgpXaGVuIGxvYWRpbmcgZGF0YSwgSSB3YW50ZWQgdG8gbmFtZSB0aGUgZGF0YWZyYW1lcyB3aXRoIG1lYW5pbmdmdWwgbmFtZXMuIFRvIHNpbXBsaWZ5IHRoZSBmaWxlIG5hbWVzLCBJIHVzZWQgdGhlIGZvbGxvd2luZyBrZXkgZm9yIHRpbWUgbWVhc3VyZW1lbnRzOgoKKiBkYWlseSAtIGQKKiBob3VybHkgLSBoCiogbWludXRlcyAtIG0KKiBzZWNvbmRzIC0gcwoqIE5hcnIgLSBOYXJyb3cKKiBXaWRlIC0gV2lkZQoKRGF0YWZyYW1lIG5hbWVzIGJlZ2luIHdpdGggYSB0aW1lIG1lYXN1cmVtZW50LCBmb2xsb3dlZCBieSBhIHBlcmlvZCBmb3IgcmVhZGFiaWxpdHksIGFuZCBhIGRlc2NyaXB0aW9uIG9mIHRoZSBoZWFsdGggbWVhc3VyZW1lbnQuCgpgYGB7ciBkYXRhIHNldHN9CiMgSSBjb21tZW50ZWQgb3V0IGRhdGFmcmFtZXMgdGhhdCB3b24ndCBiZSB1c2VkIGluIHRoaXMgYW5hbHlzaXMuCiMgVG8gdmlldyBkYXRhZnJhbWVzLCB1bmNvbW1lbnQgZWFjaCBWaWV3KGRhdGFmcmFtZV9uYW1lKSBhbmQgcmUtcnVuIGNvZGUKCiMgYWN0aXZpdHkKZC5hY3Rpdml0eSA8LSByZWFkX2NzdigiLi9kYXRhL2RhaWx5QWN0aXZpdHlfbWVyZ2VkLmNzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpIAojIFZpZXcoZC5hY3Rpdml0eSkKIyBkLmFjdGl2aXR5IGNvbnRhaW5zIHRoZSBkYXRhIGZvdW5kIHNlcGFyYXRlbHkgaW4gZC5pbnRlbnNpdGllcywgZC5jYWxvcmllcywgZC5zdGVwcwoKIyBpbnRlbnNpdGllcwojIGQuaW50ZW5zaXRpZXMgPC0gcmVhZF9jc3YoIi4vZGF0YS9kYWlseUludGVuc2l0aWVzX21lcmdlZC5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQojIFZpZXcoZC5pbnRlbnNpdGllcykKaC5pbnRlbnNpdGllcyA8LSByZWFkX2NzdigiLi9kYXRhL2hvdXJseUludGVuc2l0aWVzX21lcmdlZC5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQojVmlldyhoLmludGVuc2l0aWVzKQojIG0uaW50ZW5zaXRpZXNOYXJyIDwtIHJlYWRfY3N2KCIuL2RhdGEvbWludXRlSW50ZW5zaXRpZXNOYXJyb3dfbWVyZ2VkLmNzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpCiNWaWV3KG0uaW50ZW5zaXRpZXNOYXJyKQojIG0uaW50ZW5zaXRpZXNXaWRlIDwtIHJlYWRfY3N2KCIuL2RhdGEvbWludXRlSW50ZW5zaXRpZXNXaWRlX21lcmdlZC5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQoKCiMgY2Fsb3JpZXMKIyBkLmNhbG9yaWVzIDwtIHJlYWRfY3N2KCIuL2RhdGEvZGFpbHlDYWxvcmllc19tZXJnZWQuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKIyBWaWV3KGQuY2Fsb3JpZXMpCmguY2Fsb3JpZXMgPC0gcmVhZF9jc3YoIi4vZGF0YS9ob3VybHlDYWxvcmllc19tZXJnZWQuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKIyBWaWV3KGguY2Fsb3JpZXMpCiMgbS5jYWxvcmllc05hcnIgPC0gcmVhZF9jc3YoIi4vZGF0YS9taW51dGVDYWxvcmllc05hcnJvd19tZXJnZWQuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKIyBtLmNhbG9yaWVzV2lkZSA8LSByZWFkX2NzdigiLi9kYXRhL21pbnV0ZUNhbG9yaWVzV2lkZV9tZXJnZWQuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKIyBWaWV3KG0uY2Fsb3JpZXNXaWRlKQoKIyBzdGVwcwojIGQuc3RlcHMgPC0gcmVhZF9jc3YoIi4vZGF0YS9kYWlseVN0ZXBzX21lcmdlZC5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQojIFZpZXcoZC5zdGVwcykKaC5zdGVwcyA8LSByZWFkX2NzdigiLi9kYXRhL2hvdXJseVN0ZXBzX21lcmdlZC5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQojIFZpZXcoaC5zdGVwcykKIyBtLnN0ZXBzTmFyciA8LSByZWFkX2NzdigiLi9kYXRhL21pbnV0ZVN0ZXBzTmFycm93X21lcmdlZC5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQojIFZpZXcobS5zdGVwc05hcnIpCiMgbS5zdGVwc1dpZGUgPC0gcmVhZF9jc3YoIi4vZGF0YS9taW51dGVTdGVwc1dpZGVfbWVyZ2VkLmNzdiIsIHNob3dfY29sX3R5cGVzID0gRkFMU0UpCiMgVmlldyhtLnN0ZXBzV2lkZSkKCiMgTUVUcyAtIG1ldGFib2xpYyBlcXVpdmFsZW50IG9mIHRhc2sgKHJlbGF0ZXMgdG8gaW50ZW5zaXR5KQojIG1pbnV0ZU1FVFMgPC0gcmVhZF9jc3YoIi4vZGF0YS9taW51dGVNRVRzTmFycm93X21lcmdlZC5jc3YiLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQojIFZpZXcobWludXRlTUVUUykKCiMgaGVhcnRyYXRlCiMgcy5oZWFydHJhdGUgPC0gcmVhZF9jc3YoIi4vZGF0YS9oZWFydHJhdGVfc2Vjb25kc19tZXJnZWQuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKCiMgd2VpZ2h0CiMgd2VpZ2h0TG9nIDwtIHJlYWRfY3N2KCIuL2RhdGEvd2VpZ2h0TG9nSW5mb19tZXJnZWQuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKIyBWaWV3KHdlaWdodExvZykKCiMgc2xlZXAKc2xlZXBEYXkgPC0gcmVhZF9jc3YoIi4vZGF0YS9zbGVlcERheV9tZXJnZWQuY3N2Iiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKIyBWaWV3KHNsZWVwRGF5KQoKCmBgYAoKCkFmdGVyIGxvYWRpbmcgdGhlIGRhdGEgaW4gUiwgSSBub3RpY2VkIHNvbWUgY29sdW1uIHZhbHVlcywgbGlrZSBkaXN0YW5jZSBvciBNRVRTIHdlcmUgdmFndWUgaW4gbWVhbmluZy4gRm9yIGluc3RhbmNlLCBJIHdvbmRlcmVkLCB3aGF0IG1lYXN1cmVtZW50IGlzIHRoZSBGaXRiaXQgdHJhY2tlciB1c2luZyBmb3IgdG90YWwgZGlzdGFuY2U/IEkgZm91bmQgdGhlIFttZXRhZGF0YSBmb3IgdGhlIEZpdEJhc2UgZmlsZXNdKGh0dHBzOi8vd3d3LmZpdGFiYXNlLmNvbS9tZWRpYS8xOTMwL2ZpdGFiYXNlZGF0YWRpY3Rpb25hcnkxMDIzMjAucGRmKSwgdGhlIERhdGEgRGljdGlvbmFyeSwgb24gdGhlIEZpdEJhc2Ugd2Vic2l0ZS4gVGhpcyBtZXRhZGF0YSBoYXMgZXhwbGFuYXRpb25zIG9uIHdoYXQgZWFjaCBjb2x1bW4gdmFsdWUgbWVhbnMuIFR1cm5zIG91dCwgdGhlIHRvdGFsIGRpc3RhbmNlIGlzIG1lYXN1cmVkIGluIGtpbG9tZXRlcnMuCgoKIyMjIyBCZWxsYWJlYXQgRGV2aWNlIC0tIFRpbWUKCldoaWxlIHRoZSBsb2FkZWQgZGF0YSBpcyBhIGxvdCBvZiBmaWxlcywgSSB3aWxsIG9ubHkgYmUgYW5hbHl6aW5nIHNwZWNpZmljIGhlYWx0aCBtZWFzdXJlbWVudHMgZm9yIHRoZSBzY29wZSBvZiB0aGlzIHByb2plY3QgLS0gaW4gcGFydGljdWxhciwgdGhvc2UgdGhhdCByZWxhdGUgdG8gdGhlIEJlbGxhYmVhdCBkZXZpY2Ugb2YgY2hvaWNlLiBBZnRlciByZXZpZXdpbmcgQmVsbGFiZWF0J3MgcHJvZHVjdHMsIEkgY2hvc2UgdG8gZm9jdXMgb24gW1RpbWVdKGh0dHBzOi8vYmVsbGFiZWF0LmNvbS90aW1lLyksIGEgaGVhbHRoIGFuZCB3ZWxsbmVzcyB0cmFja2luZyB3YXRjaCBmb3I6CgoqIEFjdGl2ZSB0aW1lCiogU2xlZXAgdGltZQoqIE1pbmRmdWxuZXNzCiogU3RyZXNzIHJlc2lzdGFuY2UKKiBNZW5zdHJ1YWwgY3ljbGUKCkFkZGl0aW9uYWwgYmVuZWZpdHMgb2YgdGhlIHdhdGNoIGluY2x1ZGUgd2lyZWxlc3MgY29ubmVjdGl2aXR5IGFuZCBzeW5jaW5nIHRvIHRoZSBCZWxsYWJlYXQgYXBwLCBtZWRpdGF0aW9uIGFuZCBoeWRyYXRpb24gdHJhY2tpbmcsIGFuZCBhIHdlbGxuZXNzIHNjb3JlIGNhbGN1bGF0aW9uLCBhY2NvcmRpbmcgdG8gdGhlIHdlYnNpdGUuCgpJIHdhbnRlZCB0byBleGFtaW5lIHRyZW5kcyBmb3IgRml0Yml0IGRhdGEgb24gKiphY3Rpdml0eSoqIGFuZCAqKnNsZWVwKiogYmVjYXVzZSB0aG9zZSBoZWFsdGggbWVhc3VyZXMgYXJlIHJlbGV2YW50IHRvIHRoZSBUaW1lIHdhdGNoLiBPZiB0aGUgZGF0YXNldHMsIEkgaW5jb3Jwb3JhdGVkIHRoZSBmb2xsb3dpbmcgaW4gbXkgYW5hbHlzaXM6CgoqIGQuYWN0aXZpdHkKKiBoLmludGVuc2l0aWVzCiogaC5jYWxvcmllcwoqIGguc3RlcHMKKiBzbGVlcERheQoKVGhlc2UgZGF0YXNldHMgYXJlIGluIHdpZGUgZm9ybWF0IGFuZCBzYXZlZCBhcyBDU1ZzIGluIHRoZSBbZGF0YV0gZm9sZGVyIHdpdGhpbiB0aGUgb3ZlcmFsbCBwcm9qZWN0IGZvbGRlci4KCkZvciBpbnNwaXJhdGlvbiBvbiB3YXlzIHRvIGFuYWx5emUgYW5kIHZpc3VhbGl6ZSBGaXRiaXQgdHJhY2tpbmcgZGF0YSwgSSByZWZlcnJlZCB0byBZYXNoIFNvbmkncyB0dXRvcmlhbCwgcHVibGlzaGVkIG9uIFtmcmVlQ29kZUNhbXBdKGh0dHBzOi8vd3d3LmZyZWVjb2RlY2FtcC5vcmcvbmV3cy9ob3ctaS1hbmFseXplZC10aGUtZGF0YS1mcm9tLW15LWZpdGJpdC10by1pbXByb3ZlLW15LW92ZXJhbGwtaGVhbHRoLWEyZTM2NDI2ZDhmOS8pLiBUaGUgbWFpbiBkaWZmZXJlbmNlIHdpdGggU29uaSdzIGRhdGEgaXMgdGhhdCBpdCBpcyBzcGVjaWZpYyB0byBvbmUgaW5kaXZpZHVhbCwgd2hlcmVhcyB0aGUgc2FtcGxlIEZpdGJpdCBkYXRhIGNvbnRhaW5zIGFjdGl2aXR5IGRhdGEgZm9yIHVwIHRvIDMzIGRpZmZlcmVudCB1c2VycywgZWFjaCBhc3NpZ25lZCB3aXRoIGEgdW5pcXVlIElkLiBCdXQgYmFzZWQgb24gdGhlIHR1dG9yaWFsLCB3YXlzIEkgY2FuIGFuYWx5emUgZGF0YSBpbmNsdWRlIGJ5IGRheSBvZiB0aGUgd2VlaywgdXNpbmcgYm94cGxvdHMgb3IgYmFyIGdyYXBocywgb3IgdXNpbmcgYSBjb3JyZWxhdGlvbiBtYXRyaXguCgpJbiBhZGRpdGlvbiB0byB0aWR5dmVyc2UsIEkgdXNlZCB0aGUgZm9sbG93aW5nIFIgcGFja2FnZXM6CmBgYHtyIHBhY2thZ2VzfQoKbGlicmFyeShsdWJyaWRhdGUpICMgd3JhbmdsaW5nIGRhdGVzCmxpYnJhcnkoYXdlZWspIyBhZGRpbmcgd2VlayBudW1iZXJzIHRvIGRhdGVzCmxpYnJhcnkoamFuaXRvcikgIyBjbGVhbmluZyBkYXRhZnJhbWUgY29sdW1uIG5hbWVzCmxpYnJhcnkoZ2djb3JycGxvdCkgIyBjb3JyZWxhdGlvbiBtYXRyaXgKbGlicmFyeShzY2FsZXMpICMgdmlzdWFsaXppbmcgcGVyY2VudGFnZSBzY2FsZXMKCmBgYAoKIyMjIERhaWx5IEFjdGl2aXR5CiMjIyMgUHJvY2VzcyAtLSBDbGVhbmluZyBkYWlseSBhY3Rpdml0eSBkYXRhCgoqKjEuIEFkZGluZyBkYXkgb2Ygd2VlayBhbmQgSWQyIHRvIGQuYWN0aXZpdHkqKgoKU2luY2UgSSBwbGFubmVkIHRvIGV4YW1pbmUgdHJlbmRzIGJ5IGRheSBvZiB0aGUgd2VlaywgSSB1c2VkIHRoZSB3ZWVrZGF5cygpIGZ1bmN0aW9uIGluIGJhc2UgUiB0byBjb252ZXJ0IGVhY2ggZGF0ZSBpbnRvIHRoZSBkYXkgb2YgdGhlIHdlZWsgKGV4OiBNb25kYXkpLiBGb3IgZWFzaWVyIHJlYWRhYmlsaXR5IG9mIElkIG51bWJlcnMsIEkgcmVhc3NpZ25lZCBlYWNoIElkIHVzZXIgdG8gYSBsZXR0ZXIgb3IgYSBsZXR0ZXIgcGFpciAtLSBrbm93biBhcyBJZDIuCgpgYGB7ciB3ZWVrZGF5IHZzIHdlZWtlbmQgZXhlcmNpc2V9CmQuYWN0aXZpdHkkZGF0ZSA8LSBhcy5EYXRlKGQuYWN0aXZpdHkkQWN0aXZpdHlEYXRlLCBmb3JtYXQ9IiVtLyVkLyVZIikKI1ZpZXcoZC5hY3Rpdml0eSkKCiMgZXh0cmFjdCB3ZWVrIGRheQpkLmFjdGl2aXR5JERheSA8LSB3ZWVrZGF5cyhkLmFjdGl2aXR5JGRhdGUpCgpsaS53a2RheXMgPC0gYygiTW9uZGF5IiwgIlR1ZXNkYXkiLCAiV2VkbmVzZGF5IiwgIlRodXJzZGF5IiwgIkZyaWRheSIpCmxpLndrZW5kcyA8LSBjKCJTdW5kYXkiLCAiU2F0dXJkYXkiKQpsaS5kYXlzb2Z3ayA8LSBjKCJTdW5kYXkiLCAiTW9uZGF5IiwgIlR1ZXNkYXkiLCAiV2VkbmVzZGF5IiwgIlRodXJzZGF5IiwgIkZyaWRheSIsICJTYXR1cmRheSIpCgpkLmFjdGl2aXR5IDwtIGQuYWN0aXZpdHkgJT4lIG11dGF0ZSh3a2RheWVuZCA9IGNhc2Vfd2hlbihEYXkgJWluJSBsaS53a2RheXMgfiAid2Vla2RheSIsIERheSAlaW4lIGxpLndrZW5kcyB+ICJ3ZWVrZW5kIikpCmQuYWN0aXZpdHkkSWQgPC0gYXMuY2hhcmFjdGVyKGQuYWN0aXZpdHkkSWQpCiNWaWV3KGQuYWN0aXZpdHkpCgojIFBhaXIgZWFjaCBJZCB3aXRoIGEgdW5pcXVlIGxldHRlciBJZDIuIExFVFRFUlMgaXMgIkEiOiJaIi4gQXMgdGhlcmUgYXJlIDMzIHVzZXJzIGFuZCBvbmx5IDI2IGFscGhhYmV0IGxldHRlcnMsIGFkZCA3IG1vcmUgdW5pcXVlIGxldHRlciBwYWlyIGFzIElkMi4KSWQyIDwtIGFwcGVuZChMRVRURVJTLCBjKCJaQSIsICJaQiIsICJaQyIsICJaRCIsICJaRSIsICJaRiIsICJaRyIpKSAjIG5ldyBJZHMKSWQudW5pcXVlIDwtIGQuYWN0aXZpdHkgJT4lIHNlbGVjdChJZCkgJT4lIGRpc3RpbmN0KCkgIyBkaXN0aW5jdCBJZHMKSWQubmV3IDwtIGRhdGEuZnJhbWUoSWQudW5pcXVlLCBJZDIpCiNWaWV3KElkLm5ldykKCiMgbGVmdCBqb2luIHRvIGQuYWN0aXZpdHkKZC5hY3Rpdml0eSA8LSBtZXJnZSh4PWQuYWN0aXZpdHksIHk9SWQubmV3LCBieT0iSWQiLCBhbGwueD1UUlVFKQojVmlldyhkLmFjdGl2aXR5KQoKYGBgCgoKCioqMi4gQSB2aXN1YWwgb3ZlcnZpZXcgb2YgZC5hY3Rpdml0eSoqCgpJIHBsb3R0ZWQgYSBjb3JyZWxhdGlvbiBtYXRyaXggdG8gdmlzdWFsaXplIHBvdGVudGlhbCBjb3JyZWxhdGlvbiByZWxhdGlvbnNoaXBzIGJldHdlZW4gZGF0YSB2YWx1ZSBjb2x1bW5zLiBJIHJlZmVyZW5jZWQgQWxib3VrYWRlbCBLYXNzYW1iYXJhJ3MgZG9jdW1lbnRhdGlvbiBvZiB0aGUgW2dnY29ycnBsb3QgcGFja2FnZV0oaHR0cHM6Ly9ycGtncy5kYXRhbm92aWEuY29tL2dnY29ycnBsb3QvKSwgd2hpY2ggaXMgYWxzbyBvbiBbR2l0SHViXShodHRwczovL2dpdGh1Yi5jb20va2Fzc2FtYmFyYS9nZ2NvcnJwbG90KS4KCgpTZWRlbnRhcnkgbWludXRlcyBhbmQgZGlzdGFuY2UgaGF2ZSBsaXR0bGUgdG8gbm8gY29ycmVsYXRpb24gd2l0aCBjYWxvcmllcywgYXMgZXhwZWN0ZWQuIEZvciBlYWNoIGFjdGl2ZSBpbnRlbnNpdHkgKHZlcnksIG1vZGVyYXRlL2ZhaXIsIGxpZ2h0KSwgdGhlIG1pbnV0ZXMgYW5kIGRpc3RhbmNlIGhhdmUgYSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiwgYXMgZG8gdGhlIHRvdGFsIGRpc3RhbmNlIGFuZCB0cmFja2VyIGRpc3RhbmNlLiBXaGVuIGFuYWx5emluZyB0aGUgZGF0YSwgSSB3aWxsIGZvY3VzIG9uIHRoZSBhY3RpdmUgZXhlcmNpc2UgYW5kIGZpbHRlciBvdXQgdGhlIHNlZGVudGFyeSBtaW51dGVzIGFuZCBkaXN0YW5jZS4KCk5leHQsIEkgd2FudGVkIHRvIGtub3csIGhvdyBjb25zaXN0ZW50IGFyZSB1c2VycyByZWNvcmRpbmcgdGhlaXIgZGFpbHkgYWN0aXZpdHkgYW5kIHVzaW5nIHRoZSBGaXRiaXQgdHJhY2tlcj8gVGhlIG51bWJlciBvZiBlbnRyaWVzIHZhcmllZCBiZXR3ZWVuIHVzZXJzLCB3aGljaCBjYW4gYmUgc2VlbiB3aGVuIHBsb3R0aW5nIHRoZSBkYXRlIGFuZCBUb3RhbERpc3RhbmNlIGZvciBlYWNoIHVzZXIgaW4gdGhlIHNjYXR0ZXIgcGxvdC4gSSBpc29sYXRlZCB0aGUgdG9wIDMwIHVzZXJzIHdpdGggdGhlIG1vc3QgZGF0YS4KCmBgYHtyIG92ZXJ2aWV3IGFjdGl2aXR5LCBmaWcud2lkdGg9IDksIGZpZy5oZWlnaHQ9NX0KIyBjb3JyIG1hdHJpeCBmb3IgZC5hY3Rpdml0eSB1c2luZyBHR2FsbHkgbGlicmFyeQpkLmFjdGl2aXR5X3NlbCA8LSBkLmFjdGl2aXR5ICU+JSBzZWxlY3QoVG90YWxEaXN0YW5jZTogVHJhY2tlckRpc3RhbmNlLCBWZXJ5QWN0aXZlRGlzdGFuY2U6IENhbG9yaWVzKQoKbXRyeC5hY3Rpdml0eSA8LSByb3VuZChjb3IoZC5hY3Rpdml0eV9zZWwpLCAxKSAjIGNvcnJlbGF0aW9uIG1hdHJpeApwdmFscy5hY3Rpdml0eSA8LSBjb3JfcG1hdChkLmFjdGl2aXR5X3NlbCkgIyBwLXZhbHVlcyBtYXRyaXgKCmNvcnIuYWN0aXZpdHkgPC0gZ2djb3JycGxvdChtdHJ4LmFjdGl2aXR5LCBvdXRsaW5lLmNvbG9yID0gImdyYXkyOCIsIHR5cGU9Imxvd2VyIiwgbGVnZW5kLnRpdGxlID0gImNvcnJlbGF0aW9uIikgKwogIGdndGl0bGUoIkNvcnJlbGF0aW9uIG9mIERhaWx5IFN0ZXBzLCBNaW51dGVzLCBEaXN0YW5jZSwgYW5kIENhbG9yaWVzIikgKwogIGxhYnMoc3VidGl0bGUgPSAiRml0Yml0IGRhaWx5IGFjdGl2aXR5IGRhdGEgZm9yIDMzIHVzZXJzIGZyb20gQXByaWwgMTIgdG8gTWF5IDksIDIwMTYiLCBjYXB0aW9uPSJTb3VyY2U6IEZ1cmJlcmcgZXQgYWwuIDIwMTYuIFplbm9kby4iKSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiKSwKICAgICAgICAgIHBsb3QudGl0bGUucG9zaXRpb249ICJwbG90IiwKICAgICAgICAgIHBsb3QuY2FwdGlvbi5wb3NpdGlvbiA9ICJwbG90IikKY29yci5hY3Rpdml0eQoKIyBnZ3NhdmUoIi4vcGxvdHMvY29yci5hY3Rpdml0eS5wbmciICwgd2lkdGggPSA4LCBoZWlnaHQgPSA1LCBkcGk9MjAwKQoKIAojIEhvdyBjb25zaXN0ZW50IGlzIHRoZSBkYXRhIGZvciB1c2Vycz8KdXNlckFjdGl2aXR5Q291bnQgPC0gZC5hY3Rpdml0eSAlPiUgZ3JvdXBfYnkoSWQyKSAlPiUgCiAgc3VtbWFyaXplKENvdW50ID0gbigpKSAlPiUgYXJyYW5nZShkZXNjKENvdW50KSkKI1ZpZXcodXNlckFjdGl2aXR5Q291bnQpCgojIFBsb3R0aW5nIHRoZSBkYXRlIGFuZCBUb3RhbERpc3RhbmNlIGZvciBlYWNoIHVzZXIgc2hvd3Mgc29tZSB1c2VycyBzdG9wcGVkIGRhdGEgY29sbGVjdGlvbiBiZWZvcmUgTWF5IDA5LCAyMDE2LCB0aGUgbGFzdCBkYXkuCnNjYXR0ZXIuZGF0ZURpc3RhbmNlIDwtIGQuYWN0aXZpdHkgJT4lIGdncGxvdChhZXMoeCA9IGRhdGUsIHk9VG90YWxEaXN0YW5jZSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IENhbG9yaWVzKSwgc3RhdD0iaWRlbnRpdHkiKSArCiAgZ2d0aXRsZSgiU29tZSBGaXRiaXQgdXNlcnMgZGlkIG5vdCByZWNvcmQgZGlzdGFuY2UgZm9yIHRoZSBmdWxsIHRpbWUgcGVyaW9kIikgKwogIGxhYnMoc3VidGl0bGUgPSAiQWN0aXZpdHkgZGF0YSB3YXMgcmVjb3JkZWQgVHVlc2RheSwgQXByaWwgMTIsIDIwMTYgdG8gVGh1cnNkYXksIE1heSAxMiwgMjAxNiIsIGNhcHRpb249IlNvdXJjZTogRnVyYmVyZyBldCBhbC4gMjAxNi4gWmVub2RvLiIpICsKICBzY2FsZV94X2RhdGUoZGF0ZV9icmVha3M9IjIgd2Vla3MiLCBkYXRlX2xhYmVscz0iJWIgJWQiKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKHJldmVyc2U9VFJVRSkpICsKICBmYWNldF93cmFwKH5JZDIsIG5yb3c9NSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0Lng9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUsIHZqdXN0PTAuMyksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHZqdXN0ID0gLTAuNSksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIiksIAogICAgICAgIHBsb3QuY2FwdGlvbi5wb3NpdGlvbj0gInBsb3QiKQogCnNjYXR0ZXIuZGF0ZURpc3RhbmNlCiMgZ2dzYXZlKCIuL3Bsb3RzL3NjYXR0ZXIuZGF0ZURpc3RhbmNlLnBuZyIgLCB3aWR0aCA9IDksIGhlaWdodCA9IDUsIGRwaT0zMDApCgoKIyBEZXRlcm1pbmUgdG9wIDMwIHVzZXJzIHdpdGggbW9zdCBkYWlseSBkYXRhIGF2YWlsYWJsZQp1c2VyQWN0aXZpdHkzMCA8LSB1c2VyQWN0aXZpdHlDb3VudCAlPiUgdG9wX24oMzAsIENvdW50KSAlPiUgZ2xpbXBzZSgpCgojKHVzZXJBY3Rpdml0eTMwKQpgYGAKCgojIyMjIEFuYWx5emUgLS0gRmluZCB1c2VyIHRyZW5kcyBpbiBkYWlseSBhY3Rpdml0eQoKKioxLiBGb3IgdGhlIHRvcCAzMCB1c2VycywgSSBleGFtaW5lZCB3aGVuIHRoZXkgbW9zdCBmcmVxdWVudGx5IGV4ZXJjaXNlLiBIb3cgZG9lcyB0aGVpciBhY3Rpdml0eSBkaWZmZXIgYmV0d2VlbiBkYXlzIG9mIHRoZSB3ZWVrPyoqCgpGb3IgZXhhbXBsZXMgb24gbWFraW5nIGEgdmlvbGluIHBsb3Qgd2l0aCBhIGJveHBsb3Qgc3VwZXJpbXBvc2VkIG9uIGl0LCBJIHJlZmVycmVkIHRvIFlhbiBIb2x0eidzIHRoZSBSIGdyYXBoIGdhbGxlcnk6IFtWaW9saW4gcGxvdCB3aXRoIGluY2x1ZGVkIGJveHBsb3QgYW5kIHNhbXBsZSBzaXplIGluIGdncGxvdDJdKGh0dHBzOi8vd3d3LnItZ3JhcGgtZ2FsbGVyeS5jb20vdmlvbGluX2FuZF9ib3hwbG90X2dncGxvdDIuaHRtbCkuCgpUaGUgb3JpZ2luYWwgZGF0YSBmb3IgW2QuYWN0aXZpdHldIHdhcyBmcm9tIFR1ZXNkYXksIEFwcmlsIDEyLCAyMDE2IHRvIFRodXJzZGF5LCBNYXkgMTIsIDIwMTYuIFRvIGV4YW1pbmUgdHJlbmRzIGJ5IGRheSBvZiB3ZWVrLCBJIHNlbGVjdGVkIDIgZnVsbCB3ZWVrcyBvZiBkYXRhIHdoZXJlIDMwIHVzZXJzIGhhdmUgbG9nZ2VkIGRhdGEuIFRoaXMgd2F5LCB0aGVyZSBpcyBhbiBlcXVhbCBhbW91bnQgb2YgZGF0YSBmcm9tIGVhY2ggdXNlciBmb3IgZWFjaCBkYXkuCgoKIyMjIyBTaGFyZSAtLSBEYWlseSBhY3Rpdml0eSBieSBkYXkgb2YgdGhlIHdlZWsKClRoZSB2aW9saW4gcGxvdHMgc2hvdyBhIGJyb2FkZXIgZGlzdHJpYnV0aW9uIGluIHN0ZXBzIGFuZCBkaXN0YW5jZSBvbiB0aGUgd2Vla2VuZHMgdGhhbiB3ZWVrZGF5cywgYW5kIFR1ZXNkYXlzIGhhdmUgYSBncmVhdGVyIG1lZGlhbiBvZiBzdGVwcyBhbmQgdG90YWwgZGlzdGFuY2UuCgpgYGB7ciB0b3AzMCBhY3Rpdml0eSBieSBkYXlzLCBmaWcud2lkdGg9IDgsIGZpZy5oZWlnaHQ9NX0KCnRvcDMwLmFjdGl2aXR5IDwtIGQuYWN0aXZpdHkgJT4lIGZpbHRlcihJZDIgJWluJSB1c2VyQWN0aXZpdHkzMCRJZDIpCiNWaWV3KHRvcDMwLmFjdGl2aXR5KQoKY291bnRfZGF0ZXMgPC0gdG9wMzAuYWN0aXZpdHkgJT4lIGdyb3VwX2J5KGRhdGUpICU+JSBjb3VudCgpICU+JSBnbGltcHNlKCkKCiMgaXNvbGF0ZSBmaXJzdCB0d28gZnVsbCB3ZWVrcyAoc3RhcnRpbmcgZnJvbSBTdW5kYXkpCnRvcDMwLmFjdGl2aXR5LmZ1bGx3a3MgPC0gdG9wMzAuYWN0aXZpdHkgJT4lIGZpbHRlcihiZXR3ZWVuKGRhdGUsIGFzLkRhdGUoIjIwMTYtMDQtMTciKSwgYXMuRGF0ZSgiMjAxNi0wNC0zMCIpKSkKCmNoZWNrMSA8LSB0b3AzMC5hY3Rpdml0eS5mdWxsd2tzICU+JSBncm91cF9ieShJZDIpICU+JSBjb3VudCgpCiNWaWV3KGNoZWNrMSkKCiNWaWV3KHRvcDMwLmFjdGl2aXR5LmZ1bGx3a3MpCiMgc3RlcHMgYnkgZGF5CnZpby5zdGVwc0RheSA8LSB0b3AzMC5hY3Rpdml0eS5mdWxsd2tzICU+JSBnZ3Bsb3QoYWVzKHg9RGF5LCB5PVRvdGFsU3RlcHMpKSArCiAgZ2VvbV92aW9saW4oYWVzKGZpbGw9IHdrZGF5ZW5kKSwgYWxwaGE9MC41LCB3aWR0aD0wLjgpICsKICBnZW9tX2JveHBsb3Qod2lkdGggPTAuMywgYWVzKGZpbGw9d2tkYXllbmQpLCBhbHBoYT0wLjQpICsKICBnZ3RpdGxlKCJXaWRlciBkaXN0cmlidXRpb24gb2YgdG90YWwgc3RlcHMgb24gd2Vla2VuZHMgdGhhbiB3ZWVrZGF5cyAiLCBzdWJ0aXRsZT0iR3JlYXRlciBtZWRpYW4gc3RlcHMgb24gVHVlc2RheXMsIG9mIHRoZSB3ZWVrZGF5cyIpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IGxpLmRheXNvZndrKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAyODAwMCwgNzAwMCkpICsKICBsYWJzKHggPSJkYXkiLCB5ID0gInN0ZXBzIiwgZmlsbD0iZGF5IHR5cGUiLCBjYXB0aW9uID0gIkZpdGJpdCBkYXRhIG9mIDMwIHVzZXJzIGZvciB0d28gd2Vla3MgKFN1biwgQXByaWwgMTcgdG8gU2F0LCBBcHJpbCAzMCwgMjAxNikgXG5Tb3VyY2U6IEZ1cmJlcmcgZXQgYWwuIDIwMTYuIFplbm9kby4iKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwbG90LmNhcHRpb24ucG9zaXRpb24gPSAicGxvdCIsCiAgICAgICAgcGxvdC50aXRsZT0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiKSkKdmlvLnN0ZXBzRGF5CgojIGdnc2F2ZSgiLi9wbG90cy92aW8uc3RlcHNEYXkucG5nIiAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNSwgZHBpPTMwMCkKCgojIGRpc3RhbmNlIGJ5IGRheQp2aW8uZGlzdGFuY2VEYXkgPC0gdG9wMzAuYWN0aXZpdHkgJT4lIGdncGxvdChhZXMoeD1EYXksIHk9VG90YWxEaXN0YW5jZSkpICsKICBnZW9tX3Zpb2xpbihhZXMoZmlsbD0gd2tkYXllbmQpLCBhbHBoYT0wLjUsIHdpZHRoPTAuOCkgKwogIGdlb21fYm94cGxvdCh3aWR0aCA9MC4zLCBhZXMoZmlsbD13a2RheWVuZCksIGFscGhhPTAuNCkgKwogIGdndGl0bGUoIldpZGVyIGRpc3RyaWJ1dGlvbiBvZiB0b3RhbCBkaXN0YW5jZSBvbiB3ZWVrZW5kcyB0aGFuIHdlZWtkYXlzIiwgc3VidGl0bGU9Ik9mIHRoZSB3ZWVrZGF5cywgVHVlc2RheXMgaGF2ZSBzbGlnaHRseSBncmVhdGVyIG1lZGlhbiBkaXN0YW5jZSIpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IGxpLmRheXNvZndrKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAyOCwgNCkpICsKICBsYWJzKHggPSAiZGF5IiwgeT0idG90YWwgZGlzdGFuY2UsIGttIiwgZmlsbD0iZGF5IHR5cGUiLCBjYXB0aW9uID0gIkZpdGJpdCBkYXRhIG9mIDMwIHVzZXJzIGZvciB0d28gd2Vla3MgKFN1biwgQXByaWwgMTcgdG8gU2F0LCBBcHJpbCAzMCwgMjAxNikgXG5Tb3VyY2U6IEZ1cmJlcmcgZXQgYWwuIDIwMTYuIFplbm9kby4iKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwbG90LmNhcHRpb24ucG9zaXRpb249InBsb3QiLAogICAgICAgIHBsb3QudGl0bGU9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpCnZpby5kaXN0YW5jZURheQoKIyBnZ3NhdmUoIi4vcGxvdHMvdmlvLmRpc3RhbmNlRGF5LnBuZyIgLCB3aWR0aCA9IDgsIGhlaWdodCA9IDUsIGRwaT0zMDApCgpgYGAKCgojIyMjIEFuYWx5emUgLS0gVHJlbmRzIGJ5IEludGVuc2l0eSBvZiBBY3Rpdml0eSBieSBXZWVrCgoqKjIuIEhvdyBjb25zaXN0ZW50IGRpZCB1c2VycyBtZWV0IHdlZWtseSB0YXJnZXQgZXhlcmNpc2UgaW50ZW5zaXR5IG1pbnV0ZXM/KioKCkJhc2VkIG9uIHRoZSBbTmF0aW9uYWwgSGVhbHRoIEludGVydmlldyBTdXJ2ZXldKGh0dHBzOi8vd3d3LmhlYWx0aHlwZW9wbGUuZ292LzIwMjAvdG9waWNzLW9iamVjdGl2ZXMvdG9waWMvcGh5c2ljYWwtYWN0aXZpdHkvbmF0aW9uYWwtc25hcHNob3QpIGFuZCBbQ0RDOiBQaHlzaWNhbCBBY3Rpdml0eV0oaHR0cHM6Ly93d3cuY2RjLmdvdi9waHlzaWNhbGFjdGl2aXR5L2Jhc2ljcy9hZHVsdHMvaW5kZXguaHRtKSwgaGVhbHRoeSB0YXJnZXRzIG1pbnV0ZXMgb2Ygd2Vla2x5IGV4ZXJjaXNlIGJ5IGRpZmZlcmVudCBpbnRlbnNpdGllcyBmb3IgYWR1bHRzIGFyZToKCiAgKiAxNTArIG1pbnV0ZXMvd2VlayAtIGxpZ2h0IG9yIG1vZGVyYXRlIGFjdGl2aXR5CiAgKiA3NSsgbWludXRlcy93ZWVrIC0gdmlnb3JvdXMgYWN0aXZpdHkgKG9yLCB2ZXJ5IGFjdGl2ZSkKCkkgYW5hbHl6ZWQgbWludXRlcyBwZXIgd2VlayBieSBpbnRlbnNpdHkgYnkgZmlyc3QgY29udmVydGluZyB0aGUgZGF0ZXMgdG8gd2VlayBudW1iZXIgYW5kIHdlZWsgZGF0ZXMuIFRoZW4sIEkgZmlsdGVyZWQgZGFpbHkgYWN0aXZpdHkgZGF0YSBmb3IgYSB0aHJlZSB3ZWVrIHBlcmlvZCBiZXR3ZWVuIFN1bmRheSwgQXByaWwgMTcsIDIwMTYgYW5kIFNhdHVyZGF5LCBNYXkgMDcsIDIwMTYuIAoKSSBncm91cGVkIHRoZSBkYXRhIGJ5IHVzZXIgSWQyIGFuZCB3ZWVrIGFuZCBjYWxjdWxhdGVkIHRoZSBzdW1zIG9mIGRpc3RhbmNlcywgY2Fsb3JpZXMsIGFuZCBtaW51dGVzLiBUbyBtYXRjaCB0aGUgdGFyZ2V0IHJhdGVzLCBJIGNvbWJpbmVkIGxpZ2h0IGFuZCBtb2RlcmF0ZSBkaXN0YW5jZXMgaW50byBhIHNlcGFyYXRlIGNvbHVtbiBhbmQgZGlkIHRoZSBzYW1lIHdpdGggbWludXRlcy4KClRvIGdyb3VwIGRhdGVzIGludG8gd2VlayBudW1iZXJzIGFuZCB3ZWVrIGRhdGVzLCBJIHJlZmVyZW5jZWQgdGhlIFthd2VlayBwYWNrYWdlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvYXdlZWsvYXdlZWsucGRmKS4gSSBjb252ZXJ0ZWQgZWFjaCB3ZWVrIG51bWJlciBpbnRvIHRoZSB3ZWVrIGRhdGUgb2YgdGhlIFN1bmRheSBvZiB0aGF0IHdlZWssIG1hcmtpbmcgdGhlIHdlZWsncyBzdGFydGluZyBkYXRlLgoKCmBgYHtyIGRpc3RhbmNlICYgbWludXRlcyBieSBhY3Rpdml0eSB0eXBlIH0KCiNWaWV3KGQuYWN0aXZpdHkpCgojIENvbnZlcnQgZGF0ZSB0byB3ZWVrIG51bWJlcgpvdyA8LSBzZXRfd2Vla19zdGFydCgiU3VuZGF5IikgIyBzZXQgc3RhcnQgZGF5IG9mIHdlZWsKCiMgZnVuY3Rpb24gZm9yIGV4dHJhY3Rpbmcgd2VlayBudW1iZXIgZnJvbSBkYXRlIGFuZCBjb252ZXJ0aW5nIHRvIHdlZWsgZGF0ZQpmbi53a051bWJlciA8LSBmdW5jdGlvbihkdCkgewpkdCR3ZWVrIDwtIGRhdGUyd2VlayhkdCRkYXRlKQpkdCR3ZWVrMiA8LSBzdWIoIi4kIiwgIjEiLCBkdCR3ZWVrKSAKZHQkd2Vla19kYXRlIDwtIHdlZWsyZGF0ZShkdCR3ZWVrMikgICMgY29udmVydCB3ZWVrIHRvIFN1bmRheSBkYXRlCnJldHVybihkdCkKfQoKIyBhcHBseSB3ZWVrIG51bWJlciAvIHdlZWsgZGF0ZSBmdW5jdGlvbiB0byBkLmFjdGl2aXR5CmQuYWN0aXZpdHkgPC0gZm4ud2tOdW1iZXIoZC5hY3Rpdml0eSkKCiMgZmlsdGVyIGRhdGEgZm9yIHRocmVlIHdlZWsgcGVyaW9kCmQuYWN0aXZpdHlfZmlsIDwtIGQuYWN0aXZpdHkgJT4lIGZpbHRlcihiZXR3ZWVuKGRhdGUsIGFzLkRhdGUoIjIwMTYtMDQtMTciKSwgYXMuRGF0ZSgiMjAxNi0wNS0wNyIpKSkKI1ZpZXcoZC5hY3Rpdml0eV9maWwpCgpkLmFjdGl2aXR5X2ZpbCAlPiUgZ3JvdXBfYnkod2Vla19kYXRlKSAlPiUgCiAgc3VtbWFyaXplKG4oKSkgJT4lIGdsaW1wc2UoKSAgIyAzIHdlZWtzCgojIFN1bSBkaXN0YW5jZXMsIG1pbnV0ZXMsIGNhbG9yaWVzIGJ5IHdlZWsgYW5kIElkMi4gQ29tYmluZSBMaWdodCBhbmQgTW9kZXJhdGUgaW50byBzaW5nbGUgY29sdW1uIChGYWlybHlBY3RpdmVNaW51dGVzIGlzIGVxdWl2YWxlbnQgdG8gTW9kZXJhdGUpLgp3a1RvdGFscy5hY3Rpdml0eSA8LSBkLmFjdGl2aXR5X2ZpbCAlPiUKICBncm91cF9ieSh3ZWVrX2RhdGUsIElkMikgJT4lIAogIHN1bW1hcml6ZSh3a0Rpc3RhbmNlID0gc3VtKFRvdGFsRGlzdGFuY2UpLCB3a1RyYWNrZXJEaXN0YW5jZSA9IHN1bShUcmFja2VyRGlzdGFuY2UpLCB3a1N0ZXBzID0gc3VtKFRvdGFsU3RlcHMpLAogICAgICAgICAgICB3a0NhbG9yaWVzID0gc3VtKENhbG9yaWVzKSwgVmVyeUFjdGl2ZURpc3RhbmNlID0gc3VtKFZlcnlBY3RpdmVEaXN0YW5jZSksIExpZ2h0TW9kZXJhdGVEaXN0YW5jZSA9IHN1bShMaWdodEFjdGl2ZURpc3RhbmNlKSArIHN1bShNb2RlcmF0ZWx5QWN0aXZlRGlzdGFuY2UpLCAKICAgICAgICAgICAgVmVyeUFjdGl2ZU1pbnV0ZXMgPSBzdW0oVmVyeUFjdGl2ZU1pbnV0ZXMpLAogICAgICAgICAgICBMaWdodE1vZGVyYXRlTWludXRlcyA9IHN1bShMaWdodGx5QWN0aXZlTWludXRlcykgKyBzdW0oRmFpcmx5QWN0aXZlTWludXRlcykpCgojIGRldGVybWluZSBpZiB3ZWVrIG1pbnV0ZSB0b3RhbHMgbWVldCBoZWFsdGh5IGV4ZXJjaXNlIHRhcmdldHMKd2tUb3RhbHMuYWN0aXZpdHkgPC0gd2tUb3RhbHMuYWN0aXZpdHkgJT4lIAogIG11dGF0ZShWZXJ5TWluX3RhcmdldCA9IGNhc2Vfd2hlbihWZXJ5QWN0aXZlTWludXRlcyA8PSA3NSB+ICJubyIsIFZlcnlBY3RpdmVNaW51dGVzID49NzUgfiAieWVzIiksCiAgICAgICAgIExpZ2h0TW9kTWluX3RhcmdldCA9IGNhc2Vfd2hlbihMaWdodE1vZGVyYXRlTWludXRlcyA8IDE1MCB+ICJubyIsIExpZ2h0TW9kZXJhdGVNaW51dGVzID49MTUwIH4gInllcyIpKQoKI1ZpZXcod2tUb3RhbHMuYWN0aXZpdHkpCgojIGRldGVybWluZSAlIG9mIHdlZWtzIGVhY2ggdXNlciBpcyBjb25zaXN0ZW50CndrVGFyZ2V0cy5hY3Rpdml0eSA8LSB3a1RvdGFscy5hY3Rpdml0eSAlPiUgZ3JvdXBfYnkoSWQyKSAlPiUgCiAgc3VtbWFyaXplKExNX3RhcmdldCA9IHN1bShMaWdodE1vZE1pbl90YXJnZXQgPT0gInllcyIpLAogICAgICAgICAgICBWX3RhcmdldCA9IHN1bShWZXJ5TWluX3RhcmdldCA9PSAieWVzIikpIAoKI1ZpZXcod2tUYXJnZXRzLmFjdGl2aXR5KQoKIyBncm91cCBieSBsaWdodC1tb2RlcmF0ZSBhbmQgdmVyeSBhY3RpdmUgdGFyZ2V0cywgY2FsY3VsYXRlIGNvdW50IGFuZCBwZXJjZW50cywgdGhlbiBiaW5kIHNlcGFyYXRlIGRhdGEgc2V0cyB0b2dldGhlcgpudW1Vc2Vycy5MTSA8LSB3a1RhcmdldHMuYWN0aXZpdHkgJT4lIGdyb3VwX2J5KExNX3RhcmdldCkgJT4lIAogIHN1bW1hcml6ZShjb3VudF91c2VycyA9IG4oKSkgJT4lIG11dGF0ZShwY3RzX3VzZXJzID0gcm91bmQoKGNvdW50X3VzZXJzIC8gMzIpLCBkaWdpdHM9MiksIGFjdGl2aXR5ID0gImxpZ2h0IG9yIG1vZGVyYXRlIikKCm51bVVzZXJzLkxNIDwtIHJlbmFtZShudW1Vc2Vycy5MTSwgd2tfY291bnQgPSAiTE1fdGFyZ2V0IikKI1ZpZXcobnVtVXNlcnMuTE0pCgpudW1Vc2Vycy5WIDwtIHdrVGFyZ2V0cy5hY3Rpdml0eSAlPiUgZ3JvdXBfYnkoVl90YXJnZXQpICU+JSAKICBzdW1tYXJpemUoY291bnRfdXNlcnMgPSBuKCkpICU+JSBtdXRhdGUocGN0c191c2VycyA9IHJvdW5kKChjb3VudF91c2VycyAvIDMyKSwgZGlnaXRzPTIpLCBhY3Rpdml0eSA9ICJ2ZXJ5IikKCgpudW1Vc2Vycy5WIDwtIHJlbmFtZShudW1Vc2Vycy5WLCB3a19jb3VudCA9ICJWX3RhcmdldCIpCgpudW1Vc2Vycy53a0FjdGl2aXR5IDwtIGJpbmRfcm93cyhudW1Vc2Vycy5MTSwgbnVtVXNlcnMuVikKI1ZpZXcobnVtVXNlcnMud2tBY3Rpdml0eSkKYGBgCgoKCiMjIyMgU2hhcmUgLS0gV2Vla2x5IEFjdGl2aXR5IFRhcmdldCBUaW1lcwpJbiB0aGUgYmFyIGdyYXBoLCBvdmVyIHRocmVlIHF1YXJ0ZXJzIG9mIHVzZXJzIGFjaGlldmVkIHRoZSB3ZWVrbHkgZ29hbCBvZiAxNTArIG1pbnV0ZXMgb2YgbGlnaHQtdG8tbW9kZXJhdGUgZXhlcmNpc2UgcGVyIHdlZWsgZm9yIGFsbCB0aHJlZSB3ZWVrcy4gQXJvdW5kIDMwJSBvZiB1c2VycyBleGVyY2lzZWQgbW9yZSB0aGFuIDc1KyBtaW51dGVzIHBlciB3ZWVrIGZvciBhbGwgdGhyZWUgd2Vla3MsIHdoaWxlIGFib3V0IDMwJSBvZiB1c2VycyBoYWQgMCB3ZWVrcy4KCkJhc2VkIG9uIHRoaXMgc2FtcGxlLCBtb3N0IHBlb3BsZSBtZWV0IHRoZSB3ZWVrbHkgZXhlcmNpc2UgcmVjb21tZW5kYXRpb25zIGZvciBsaWdodC10by1tb2RlcmF0ZSBleGVyY2lzZSwgd2hpbGUgdmVyeSBhY3RpdmUgZXhlcmNpc2UgaXMgbW9yZSB2YXJpYWJsZSBhbW9uZyB1c2Vycy4KYGBge3IgcGxvdCB3ZWVrbHkgYWN0aXZpdHl9CiMgdmlzdWFsaXplIHBlcmNlbnRhZ2UgcmVzdWx0cyBpbiBiYXIgZ3JwYWgsIGZhY2V0IHdyYXAgYnkgYWN0aXZpdHkgdHlwZQpiYXIud2tBY3Rpdml0eSA8LSBudW1Vc2Vycy53a0FjdGl2aXR5ICU+JSAKICBnZ3Bsb3QoYWVzKHggPSB3a19jb3VudCwgeSA9IHBjdHNfdXNlcnMpKSArCiAgZ2VvbV9iYXIoYWVzKGZpbGw9YWN0aXZpdHkpLCBzdGF0PSJpZGVudGl0eSIpICsgCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGNvdW50X3VzZXJzKSwgdmp1c3Q9MS4zKSArCiAgZ2d0aXRsZSgiSW4gdGhyZWUgd2Vla3M6IG1vc3QgdXNlcnMgbWV0IGxvdy1tb2RlcmF0ZSBhY3Rpdml0eSBnb2FscywgXG4gYWJvdXQgYSB0aGlyZCBtZXQgdmVyeSBhY3RpdmUgZ29hbHMiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OmxhYmVsX3BlcmNlbnQoYWNjdXJhY3k9MSkpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJwYWxlZ3JlZW4zIiwgInNpZW5uYTIiKSwgbGFiZWxzPSBjKCJsb3cgb3IgbW9kZXJhdGUgKD49MTUwIG1pbikiLCAidmVyeSAoPj03NSBtaW4pIikpICsKICBsYWJzKHN1YnRpdGxlID0gIkZpdGJpdCBtaW51dGUgaW50ZW5zaXR5IGRhdGEgZm9yIHRocmVlIHdlZWtzIChTdW4sIEFwcmlsIDE3IHRvIFNhdCwgTWF5IDA3LCAyMDE2KSIsIHggPSAid2Vla3MiLCB5ID0gInVzZXJzICgzMiB0b3RhbCkiLCBjYXB0aW9uID0gIkxhYmVscyBvbiBiYXJzIGFyZSBudW1iZXIgb2YgdXNlcnMgXG5Tb3VyY2U6IEZ1cmJlcmcgZXQgYWwuIDIwMTYuIFplbm9kby4iLCBmaWxsID0gIndlZWtseSBhY3Rpdml0eSBnb2FscyIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBsb3QuY2FwdGlvbi5wb3NpdGlvbj0icGxvdCIsCiAgICAgICAgcGxvdC50aXRsZT0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiKSkgKwogIGZhY2V0X3dyYXAofmFjdGl2aXR5KSArCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKDAsIDEpKQogIApiYXIud2tBY3Rpdml0eQoKIyBnZ3NhdmUoIi4vcGxvdHMvYmFyLndrQWN0aXZpdHkucG5nIiAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNSwgZHBpPTMwMCkKCmBgYAoKIyMjIEFuYWx5emUgLS0gSW50ZW5zaXR5IE1pbnV0ZSBUcmVuZHMgCgoqKjMuIE1pbnV0ZXMgb2YgRGlmZmVyZW50IEludGVuc2l0eSBFeGVyY2lzZSBhbmQgQ2Fsb3JpZXMqKgoKQnVpbGRpbmcgb24gc3RlcCAyLCBJIGdyb3VwZWQgdXNlcnMgYnkgd2hldGhlciBvciBub3QgdGhleSBoYWQgYSBtYWpvcml0eSBvZiB3ZWVrcyAoYXQgbGVhc3QgMiBvdXQgb2YgMyB3ZWVrcykgb2YgbGlnaHQtbW9kZXJhdGUgZXhlcmNpc2UgYW5kL29yIHZlcnktYWN0aXZlIGV4ZXJjaXNlLiBGb3IgaW5zdGFuY2UsIExNViBtZWFucyB0aGF0IHVzZXIgZXhlcmNpc2VkIGVub3VnaCBmb3IgYXQgbGVhc3QgMiB3ZWVrcywgbWVldGluZyBib3RoIGxpZ2h0LW1vZGVyYXRlIGFuZCB2ZXJ5IGFjdGl2ZSBleGVyY2lzZSB3ZWVrbHkgdGFyZ2V0IG1pbnV0ZXMuCgpPbiBhZGRpbmcgcmVncmVzc2lvbiBsaW5lcyB0byBzY2F0dGVyIHBsb3RzLCBJIHJlZmVycmVkIHRvICJCYXNpYyBSIEd1aWRlIGZvciBOU0MgU3RhdGlzdGljcyIgYnkgRGVhbm5hIExpLCBbQ2hhcHRlciAxOTogU2NhdHRlcnBsb3RzIGFuZCBCZXN0IEZpdCBMaW5lcyAtIFR3byBTZXRzXShodHRwczovL2Jvb2tkb3duLm9yZy9kbGkvcmd1aWRlL3NjYXR0ZXJwbG90cy1hbmQtYmVzdC1maXQtbGluZXMtdHdvLXNldHMuaHRtbCkuCgpgYGB7ciBncm91cCB1c2VycyBieSBpbnRlbnNpdHl9CiNWaWV3KHdrVGFyZ2V0cy5hY3Rpdml0eSkKCnVzZXJUeXBlcy5pbnRlbnNpdHkgPC0gd2tUYXJnZXRzLmFjdGl2aXR5ICU+JSAKICBtdXRhdGUodXNlcl90eXBlID0gY2FzZV93aGVuKExNX3RhcmdldCA+PSAyICYgVl90YXJnZXQgPj0gMiB+ICJMTVYiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTE1fdGFyZ2V0IDwgMiAmIFZfdGFyZ2V0IDwgMiB+ICJub3QgYWN0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExNX3RhcmdldCA+PSAyICYgVl90YXJnZXQgPCAyIH4gIkxNIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExNX3RhcmdldCA8IDIgJiBWX3RhcmdldCA+PSAyIH4gIlYiKSkKCnVzZXJUeXBlcyA8LSB1c2VyVHlwZXMuaW50ZW5zaXR5ICU+JSBzZWxlY3QoSWQyLCB1c2VyX3R5cGUpCgp1c2VyVHlwZXMgJT4lIGdyb3VwX2J5KHVzZXJfdHlwZSkgJT4lIHN1bW1hcml6ZShjb3VudCA9IG4oKSkgJT4lIGdsaW1wc2UoKQojVmlldyh1c2VyVHlwZXMpCgoKIyBsZWZ0IGpvaW4gd2tUb3RhbHMuYWN0aXZpdHkgYW5kIHVzZXJUeXBlcwp3a1RvdGFscy5hY3Rpdml0eSA8LSBtZXJnZSh4ID0gd2tUb3RhbHMuYWN0aXZpdHksIHkgPSB1c2VyVHlwZXMsIGJ5PSJJZDIiLCBhbGwueD1UUlVFKQoKd2tUb3RhbHMuYWN0aXZpdHkgPC0gd2tUb3RhbHMuYWN0aXZpdHkgJT4lIG11dGF0ZShUb3RhbEFjdGl2ZU1pbnV0ZXMgPSBWZXJ5QWN0aXZlTWludXRlcyArIExpZ2h0TW9kZXJhdGVNaW51dGVzKQoKI1ZpZXcod2tUb3RhbHMuYWN0aXZpdHkpCgpgYGAKCgoKIyMjIyBTaGFyZSAtLSBXZWVrbHkgSW50ZW5zaXR5IE1pbnV0ZSBUcmVuZHMKYGBge3IgcGxvdCB3ZWVrbHkgaW50ZW5zaXR5IG1pbnV0ZXN9CiMgb3ZlcnZpZXcgb2YgdXNlcnMgYW5kIGludGVuc2l0eSBtaW51dGVzIGFuZCBjYWxvcmllcwpzY2F0dGVyLndrTWludXRlcyA8LSB3a1RvdGFscy5hY3Rpdml0eSAlPiUgCiAgZ2dwbG90KGFlcyh4PUxpZ2h0TW9kZXJhdGVNaW51dGVzLCB5ID0gVmVyeUFjdGl2ZU1pbnV0ZXMsIGNvbG9yID0gdXNlcl90eXBlKSkgKwogIGdlb21fcG9pbnQoYWVzKHNpemUgPSB3a0NhbG9yaWVzLCBhbHBoYSA9d2tDYWxvcmllcykpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSA3NSwgbGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yPSJyZWQiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMTUwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArCiAgZ2d0aXRsZSgiV2Vla2x5IHZlcnkgYWN0aXZlIHZzLiBsaWdodC1vci1tb2RlcmF0ZSBleGVyY2lzZSwgYW5kIGNhbG9yaWVzIGJ1cm5lZCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicGFsZWdyZWVuMyIsICJzaWVubmEyIiwgImxpZ2h0Z29sZGVucm9kMiIpLCBsYWJlbHM9IGMoImxvdyBvciBtb2RlcmF0ZSIsICJsb3csIG1vZGVyYXRlLCBvciB2ZXJ5IiwgIm5vdCBhY3RpdmUiKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDAsIDI3MDAsIDQwMCkpICsKICBsYWJzKHN1YnRpdGxlID0gIkZpdGJpdCBtaW51dGUgaW50ZW5zaXR5IGRhdGEgZm9yIHRocmVlIHdlZWtzIChTdW4sIEFwcmlsIDE3IHRvIFNhdCwgTWF5IDA3LCAyMDE2KSIsIHggPSAibGlnaHQtbW9kZXJhdGUgYWN0aXZlIG1pbnV0ZXMiLCB5PSAidmVyeSBhY3RpdmUgbWludXRlcyIsIHNpemUgPSAiY2Fsb3JpZXMiLCBhbHBoYSA9ICJjYWxvcmllcyIsIGNvbG9yID0gInR5cGUgb2YgZXhlcmNpc2VyIiwgY2FwdGlvbiA9ICJEb3R0ZWQgbGluZXMgYXJlIHdlZWtseSBhY3Rpdml0eSBnb2FscyBcblNvdXJjZTogRnVyYmVyZyBldCBhbC4gMjAxNi4gWmVub2RvLiIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBsb3QuY2FwdGlvbi5wb3NpdGlvbiA9ICJwbG90IiwKICAgICAgICBwbG90LnRpdGxlPSBlbGVtZW50X3RleHQoZmFjZT0iYm9sZCIpKQpzY2F0dGVyLndrTWludXRlcwoKIyBnZ3NhdmUoIi4vcGxvdHMvc2NhdHRlci53a01pbnV0ZXMucG5nIiAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNSwgZHBpPTMwMCkKCiMgcGxvdHRpbmcgd2Vla2x5IGRpc3RhbmNlIGFuZCBtaW51dGVzIGJ5IHVzZXIgdHlwZSwgZmlsdGVyIG91dCAibm90IGFjdGl2ZSIgdXNlcnMKc2NhdHRlci53a0Rpc3RhbmNlTWludXRlcyA8LSB3a1RvdGFscy5hY3Rpdml0eSAlPiUgZmlsdGVyKHVzZXJfdHlwZSAhPSAibm90IGFjdGl2ZSIpICU+JSAKICBnZ3Bsb3QoYWVzKHg9VG90YWxBY3RpdmVNaW51dGVzLCB5ID0gd2tEaXN0YW5jZSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IHVzZXJfdHlwZSwgYWxwaGEgPSB3a0NhbG9yaWVzLCBzaXplID0gd2tDYWxvcmllcykpICsKICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgc2UgPSBGQUxTRSwgYWVzKGNvbG9yID0gdXNlcl90eXBlKSwgYWxwaGEgPSAwLjIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygicGFsZWdyZWVuMyIsICJzaWVubmEyIiksIGxhYmVscz0gYygibG93IG9yIG1vZGVyYXRlIiwgImxvdywgbW9kZXJhdGUsIG9yIHZlcnkiKSkgKwogIGdndGl0bGUoIk1vcmUgQ2Fsb3JpZXMgQnVybmVkIHdpdGggTW9yZSBXZWVrbHkgRXhlcmNpc2UiKSArCiAgbGFicyhzdWJ0aXRsZT0iVmVyeSBBY3RpdmUgRXhlcmNpc2VycyBBY2hpZXZlIEZhcnRoZXIgRGlzdGFuY2VzIGluIExlc3MgVGltZSIsIHggPSAidG90YWwgYWN0aXZlIG1pbnV0ZXMiLCB5ID0gInRvdGFsIHdlZWsgZGlzdGFuY2UsIGttIiwgc2l6ZSA9ICJ3ZWVrIGNhbG9yaWVzIiwgYWxwaGEgPSAid2VlayBjYWxvcmllcyIsIGNvbG9yID0gInR5cGUgb2YgZXhlcmNpc2VyIiwgY2FwdGlvbiA9ICJGaXRiaXQgZGF0YSBmb3IgdGhyZWUgd2Vla3MgKFN1biwgQXByaWwgMTcgdG8gU2F0LCBNYXkgMDcsIDIwMTYpIFxuU291cmNlOiBGdXJiZXJnIGV0IGFsLiAyMDE2LiBaZW5vZG8uIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGxvdC5jYXB0aW9uLnBvc2l0aW9uID0gInBsb3QiLAogICAgICAgIHBsb3QudGl0bGU9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpCnNjYXR0ZXIud2tEaXN0YW5jZU1pbnV0ZXMKCiMgZ2dzYXZlKCIuL3Bsb3RzL3NjYXR0ZXIud2tEaXN0YW5jZU1pbnV0ZXMucG5nIiAsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNSwgZHBpPTMwMCkKCgpgYGAKClRoZSBmaXJzdCBzY2F0dGVyIHBsb3Qgc2hvd3MgZWFjaCB3ZWVrbHkgb2JzZXJ2YXRpb24gYnkgdGhlIG1pbnV0ZXMgb2YgbGlnaHQtbW9kZXJhdGUgYWN0aXZlIG1pbnV0ZXMgYW5kIHZlcnkgYWN0aXZlIG1pbnV0ZXMsIHdpdGggdGhlIGNhbG9yaWVzIHJlcHJlc2VudGVkIGFzIHNpemUgb2YgdGhlIGRhdGEgcG9pbnQuIENvbG9ycyByZXByZXNlbnQgdGhlIHR5cGUgb2YgZXhlcmNpc2UgdGhhdCBlYWNoIHVzZXIgZnJlcXVlbnRseSBtZWV0cyB0YXJnZXQgd2Vla2x5IG1pbnV0ZXMgZm9yLgoKSW4gdGhlIHNlY29uZCBzY2F0dGVyIHBsb3QsIHZlcnkgYWN0aXZlIHVzZXJzIChMTVYpIHNwZW5kIG1vcmUgdG90YWwgYWN0aXZlIG1pbnV0ZXMgYW5kIGRpc3RhbmNlIGV4ZXJjaXNpbmcgcGVyIHdlZWsgdGhhbiAoTE0pIHVzZXJzIHdobyBhcmVuJ3QgdmVyeSBpbnRlbnNlIGV4ZXJjaXNlcnMuIE1vcmUgd2Vla2x5IGFjdGl2ZSBtaW51dGVzIGlzIHBvc2l0aXZlbHkgY29ycmVsYXRlZCB3aXRoIHdlZWtseSBkaXN0YW5jZSwgYW5kIGJvdGggYXJlIGNvcnJlbGF0ZWQgd2l0aCBncmVhdGVyIGV4cGVuc2Ugb2Ygd2Vla2x5IGNhbG9yaWVzLgoKIyMjIyMgS2V5IEZpbmRpbmdzIC0tIERhaWx5IEFjdGl2aXR5CgoqIEl0IHdvdWxkIGJlIGhlbHBmdWwgZm9yIEJlbGxhYmVhdCB1c2VycyB0byBoYXZlIGEgZ3JhcGggc3VtbWFyeSBvZiBtaW51dGVzIG9mIGxpZ2h0L21vZGVyYXRlIGV4ZXJjaXNlIGFuZCB2ZXJ5IGFjdGl2ZSBleGVyY2lzZSwgaW5jbHVkaW5nIGRhaWx5IGJyZWFrZG93biBhbmQgd2Vla2x5IHRvdGFscy4gSW5jbHVkaW5nIGEgd2Vla2x5IHRhcmdldCB0aW1lIGdvYWwgcmVtaW5kZXIgb2YgYm90aCBleGVyY2lzZSBpbnRlbnNpdGllcyBjb21wYXJlZCB0byB1c2Vycycgd2Vla2x5IHRvdGFscyBjb3VsZCBlbmNvdXJhZ2UgcGVvcGxlIHRvIGltcHJvdmUgb3IgbWFpbnRhaW4gdGhlaXIgZXhlcmNpc2UgYWN0aXZpdHkgb24gYSBkYWlseSBhbmQgd2Vla2x5IGJhc2lzLgoKKiBCYXNlZCBvbiB0aGUgRml0Yml0IGRhdGEsIGEgdHJlbmQgaXMgZmV3ZXIgZXhlcmNpc2VycyB3aG8gbWVldCB2ZXJ5IGFjdGl2ZSB3ZWVrbHkgZ29hbHMsIHdoZXJlYXMgYWNoaWV2aW5nIGxpZ2h0LXRvLW1vZGVyYXRlIHRhcmdldCB0aW1lcyBwZXIgd2VlayBpcyByZWxhdGl2ZWx5IG1vcmUgY29tbW9uIGFtb25nIHRoZSB1c2VyIHBvcHVsYXRpb24uIEJ1dCBwZW9wbGUgd2hvIGRvIHNwZW5kIG1vcmUgdGltZSBleGVyY2lzaW5nIHZlcnkgYWN0aXZlbHkgYXJlIGFibGUgdG8gbG9zZSBtb3JlIGNhbG9yaWVzIGluIGEgc2hvcnRlciBkaXN0YW5jZSBhbmQgdGltZSB0aGFuIHBlb3BsZSB3aG8gY29uc2lzdGVudGx5IHBlcmZvcm0gbGlnaHQtdG8tbW9kZXJhdGUgYWN0aXZpdHkuIFRoaXMgbWVhbnMgbW9yZSBpbnRlbnNlIHdvcmtvdXRzIGNhbiBiZSBhIHRpbWUgc2F2ZXIgd2l0aCBhZGRlZCBoZWFsdGggYmVuZWZpdC4gUHJvdmlkaW5nIHRoaXMgaW5mb3JtYXRpb24gdG8gdXNlcnMgY291bGQgaGVscCB0aGVtIHNldCBvdmVyYWxsIGV4ZXJjaXNlIGdvYWxzLgoKCiMjIyBIb3VybHkgQWN0aXZpdHkgRGF0YQojIyMjICBQcmVwYXJlIC0tIE1lcmdlIGhvdXJseSBhY3Rpdml0eSBkYXRhc2V0cwoKKioxLiBNZXJnaW5nIGFuZCBmaWx0ZXJpbmcgc3RlcHMsIGNhbG9yaWVzLCBpbnRlbnNpdHkgW3NjaV0gZGF0YSoqCgpMb29raW5nIGF0IHRoZSBvdGhlciBkYXRhIHNldHMgYmV5b25kIGRhaWx5IGFjdGl2aXR5LCBJIG5vdGljZWQgSSBjb3VsZCBjb21iaW5lIHRoZSBkYXRhIGluIGhvdXJseSBjYWxvcmllcywgaG91cmx5IHN0ZXBzLCBhbmQgaG91cmx5IGludGVuc2l0eSBpbnRvIG9uZSBkYXRhIGZyYW1lLiBJIHRoZW4gZmlsdGVyZWQgdGhlIGRhdGEgdG8gb25seSBob3VycyB3aGVuIHBlb3BsZSBhcmUgcmVndWxhcmx5IG1vdmluZyBhbmQgZXhlcmNpc2luZywgYmV0d2VlbiA2IEFNIGFuZCA5IFBNLCBhbmQgYWdhaW4sIGZvciBkYXRlcyBiZXR3ZWVuIGEgdGhyZWUgd2VlayBwZXJpb2QsIFN1bmRheSwgQXByaWwgMTcsIDIwMTYgYW5kIFNhdHVyZGF5LCBNYXkgMDcsIDIwMTYuCgpgYGB7ciBob3VycyBvZiBleGVyY2lzaW5nfQojVmlldyhoLmNhbG9yaWVzKQojVmlldyhoLnN0ZXBzKQojVmlldyhoLmludGVuc2l0aWVzKQoKZm4uY291bnRJZCA8LSBmdW5jdGlvbihkdCkgewogIGR0IDwtIGR0ICU+JSBjb3VudChJZCkKICByZXR1cm4oZ2xpbXBzZShkdCkpCn0KCmZuLmNvdW50SWQoaC5jYWxvcmllcykgJT4lIGdsaW1wc2UoKSAjMzMKCiNWaWV3KHNjKQojIHN0ZXBzICsgY2Fsb3JpZXMKc2MgPC0gbWVyZ2UoaC5zdGVwcywgaC5jYWxvcmllcywgYnk9YygiSWQiLCJBY3Rpdml0eUhvdXIiKSkKIyBzdGVwcyArIGNhbG9yaWVzICsgaW50ZW5zaXRpZXMKc2NpIDwtIG1lcmdlKHNjLCBoLmludGVuc2l0aWVzLCBieT1jKCJJZCIsIkFjdGl2aXR5SG91ciIpKQpzY2kgPC0gbWVyZ2Uoc2NpLCBJZC5uZXcsIGJ5PSJJZCIsIGFsbC54PVRSVUUpICMgYWRkIElkMgojVmlldyhzY2kpCgoKIyBzZXQgdGltZXpvbmUKU3lzLnNldGVudih0ej0iQW1lcmljYS9OZXdfWW9yayIpClN5cy50aW1lem9uZSgpCgoKIyBjb252ZXJ0IEFjdGl2aXR5SG91ciB0byBkYXRldGltZSBmb3JtYXQKc2NpJGRhdGV0aW1lIDwtc3RycHRpbWUoc2NpJEFjdGl2aXR5SG91ciwgIiVtLyVkLyVZICVJOiVNOiVTICVwIikKCiMgZXh0cmFjdCBob3VyIG9mIGRheSBmcm9tIHRpbWUsIGlzb2xhdGUgaG91cgpmbi5leHRyYWN0SHIgPC0gZnVuY3Rpb24oZHQpewogIGR0JGRhdGUgPC0gYXMuRGF0ZShkdCRkYXRldGltZSwgZm9ybWF0PSIlbS8lZC8lWSIpCiAgZHQkdGltZSA8LSBhcy5QT1NJWGN0KGR0JGRhdGV0aW1lLCBmb3JtYXQ9IiVIOiVNICVwIikKICBkdCRob3VyIDwtIGZvcm1hdChkdCR0aW1lLCBmb3JtYXQ9IiVJICVwIikKICBkdCRob3VyX21pbiA8LSBmb3JtYXQoZHQkdGltZSwgZm9ybWF0PSIlSTolTSAlcCIpCiAgcmV0dXJuKGR0KQp9CgoKc2NpIDwtIGZuLmV4dHJhY3RIcihzY2kpCiNWaWV3KHNjaSkKc2NpJERheSA8LSB3ZWVrZGF5cyhzY2kkZGF0ZSkKCiMgcmFuZ2Ugb2YgaG91cnMgd2hlbiBtb3N0IHBlb3BsZSBhcmUgbW92aW5nIChub3Qgc2xlZXBpbmcpCmFtSHJzIDwtIHBhc3RlKDY6OSwgIkFNIiwgc2VwPSIgIikKYW1IcnMgPC0gcGFzdGUwKDAsIGFtSHJzKQptaWRIcnMgPC0gcGFzdGUoMTA6MTEsICJBTSIsIHNlcD0iICIpCnBtSHJzIDwtIHBhc3RlKDE6OSwgIlBNIiwgc2VwPSIgIikKcG1IcnMgPC0gcGFzdGUwKDAsIHBtSHJzKQptb3ZpbmdIcnMgPC0gYyhhbUhycywgbWlkSHJzLCAiMTIgUE0iLCBwbUhycykKI2dsaW1wc2UobW92aW5nSHJzKQoKIyBmaWx0ZXIgc2NpIGRhdGEgZm9yIHNlbGVjdCBob3VycyBhbmQgZmlsdGVyIGZvciBkYXRlcyBiZXR3ZWVuIApzY2lfZmlsIDwtIHNjaSAlPiUgZmlsdGVyKGhvdXIgJWluJSBtb3ZpbmdIcnMpICU+JSBmaWx0ZXIoYmV0d2VlbihkYXRlLCBhcy5EYXRlKCIyMDE2LTA0LTE3IiksIGFzLkRhdGUoIjIwMTYtMDUtMDciKSkpCiNWaWV3KHNjaV9maWwpCgoKIyBleGFtaW5lIElkMiBpbiBzY2lfZmlsIC0gRG9lcyBpdCBtYXRjaCB1c2VycyBpbiB1c2VyVHlwZXM/IFlFUwpJZC5zY2lfZmlsIDwtIHNjaV9maWwgJT4lIGdyb3VwX2J5KElkMikgJT4lIHN1bW1hcml6ZShjb3VudCA9IG4oKSkgIyAzMiB1c2VycwojVmlldyhJZC5zY2lfZmlsKQojVmlldyh1c2VyVHlwZXMpCgp0ZXN0SWRtYXRjaCA8LSBtZXJnZSh4PUlkLnNjaV9maWwsIHkgPSB1c2VyVHlwZXMsIGJ5PSJJZDIiLCBhbGwueCA9IFRSVUUgKQojVmlldyh0ZXN0SWRtYXRjaCkKCiMgbWVyZ2Ugc2NpIHdpdGggdXNlclR5cGVzCnNjaV9maWwgPC0gbWVyZ2UoeD1zY2lfZmlsLCB5PXVzZXJUeXBlcywgYnk9IklkMiIsIGFsbC54ID0gVFJVRSApCmBgYAoKIyMjIyBBbmFseXplIC0tIEdyb3VwIGRhdGEgYnkgaG91ciBhbmQgZGF5CgoqKjIuIFdoYXQgaG91cnMgb2YgZWFjaCBkYXkgYXJlIHBlb3BsZSBleGVyY2lzaW5nPyoqCgpBZnRlciBwcmVwcGluZyB0aGUgY29tYmluZWQgaG91cmx5IHN0ZXBzLCBjYWxvcmllcywgYW5kIGludGVuc2l0eSBbc2NpX2ZpbF0gZm9yIGFuYWx5c2lzLCBJIGFuYWx5emVkIGFjdGl2aXR5IHRyZW5kcyBieSBob3VyIG9mIGRheSB0byBzZWUgd2hlbiB1c2VycyB3ZXJlIGV4ZXJjaXNpbmcgdGhlIG1vc3QgYW5kIGZvdW5kIG1lYW4gYXZlcmFnZXMgZm9yIGVhY2ggbWVhc3VyZW1lbnQuCgoKYGBge3IgYW5hbHl6ZSBzY2l9CiNWaWV3KHNjaV9maWwpCgpmbi5ob3VyRGF5LmF2Z3MgPC0gZnVuY3Rpb24oZHQpIHsKICBkdCA8LSBkdCAlPiUgZ3JvdXBfYnkoaG91ciwgRGF5KSAlPiUgCiAgICBzdW1tYXJpemUoYXZnU3RlcFRvdGFsID0gbWVhbihTdGVwVG90YWwpLCBhdmdDYWxvcmllcyA9IG1lYW4oQ2Fsb3JpZXMpLAogICAgICAgICAgICBhdmdUb3RhbEludGVuc2l0eSA9IG1lYW4oVG90YWxJbnRlbnNpdHkpKSAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCByb3VuZCkpCiAgcmV0dXJuKGR0KQp9CiAgCnNjaUF2Z3MuRGF5SG91ciA8LSBmbi5ob3VyRGF5LmF2Z3Moc2NpX2ZpbCkKI1ZpZXcoc2NpQXZncy5EYXlIb3VyKQoKCmBgYAoKCgojIyMjIFNoYXJlIC0tIEludGVuc2l0eSBoZWF0bWFwcyBieSBob3VyIGFuZCBkYXkgb2Ygd2VlawoKSSBwbG90dGVkIHRoZSBob3VybHkgYWN0aXZpdHkgZGF0YSBbc2NpXSwgdXNpbmcgZGlmZmVyZW50IGhlYXRtYXBzLiBXaGVuIG1ha2luZyB0aGUgaGVhdG1hcHMsIEkgcmVmZXJlbmNlZCBZYW4gSG9sdHoncyBbdGhlIFIgR3JhcGggR2FsbGVyeSdzIGhlYXRtYXAgdHV0b3JpYWxdKGh0dHBzOi8vd3d3LnItZ3JhcGgtZ2FsbGVyeS5jb20vNzktbGV2ZWxwbG90LXdpdGgtZ2dwbG90Mi5odG1sKSwgYXMgd2VsbCBhcyB0aGUgW3Bvc3Qgb24gUkNvbG9yQnJld2VyJ3MgcGFsZXR0ZXNdKGh0dHBzOi8vd3d3LnItZ3JhcGgtZ2FsbGVyeS5jb20vMzgtcmNvbG9yYnJld2Vycy1wYWxldHRlcy5odG1sKSwgd2hpY2ggd2FzIGhlbHBmdWwgaW4gc2VsZWN0aW5nIHRoZSBjb2xvcnMgZm9yIHRoZSBoZWF0bWFwcy4KCmBgYHtyIHBsb3Qgc2NpIGFuYWx5c2lzLCBmaWcud2lkdGg9OC41fQoKIyBPdmVyYWxsIGF2ZXJhZ2UgdG90YWwgaW50ZW5zaXRpZXMgYnkgaG91ciBhbmQgZGF5IG9mIHRoZSB3ZWVrCmhlYXQuRGF5SG91ci5pbnRlbnNpdHkgPC0gc2NpQXZncy5EYXlIb3VyICU+JSBnZ3Bsb3QoYWVzKHggPSBob3VyLCB5PURheSwgZmlsbD1hdmdUb3RhbEludGVuc2l0eSkpICsKICBnZW9tX3RpbGUoKSArCiAgZ2d0aXRsZSgiT24gQXZlcmFnZSwgTW9yZSBJbnRlbnNlIEFjdGl2aXR5IG9uIFdlZG5lc2RheXMgYWZ0ZXIgNSBwLm0uLCBTYXR1cmRheSAxIHAubS4sIFR1ZXNkYXlzIGF0IE5vb24iKSArCiAgbGFicyhmaWxsID0gImF2ZXJhZ2UgaW50ZW5zaXR5Iiwgc3VidGl0bGU9Ik1vcmUgaW50ZW5zZSB3b3Jrb3V0cyBhZnRlciA1IHAubS4gaW4gZWFybGllciB3ZWVrZGF5cyBvZiB0aGUgd2VlayAoTW9uLi0gV2VkLikiLCBjYXB0aW9uID0gIkZpdGJpdCBkYXRhIGZvciB0aHJlZSB3ZWVrcyAoU3VuLCBBcHJpbCAxNyB0byBTYXQsIE1heSAwNywgMjAxNikgXG5Tb3VyY2U6IEZ1cmJlcmcgZXQgYWwuIDIwMTYuIFplbm9kby4iLCB5PSJkYXkiKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHM9IG1vdmluZ0hycykgKwogIHNjYWxlX3lfZGlzY3JldGUobGltaXRzPSByZXYobGkuZGF5c29md2spKSArCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIocGFsZXR0ZT0iT3JhbmdlcyIsIGRpcmVjdGlvbiA9MSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGxvdC5jYXB0aW9uLnBvc2l0aW9uID0gInBsb3QiLAogICAgICAgIHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsCiAgICAgICAgcGxvdC50aXRsZT0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiKSkKCmhlYXQuRGF5SG91ci5pbnRlbnNpdHkKCiMgZ2dzYXZlKCIuL3Bsb3RzL2hlYXQuRGF5SG91ci5pbnRlbnNpdHkucG5nIiAsIHdpZHRoID0gMTAuNSAsIGhlaWdodCA9IDcsIGRwaT0zMDApCgojIE92ZXJhbGwgYXZlcmFnZSB0b3RhbCBzdGVwcyBieSBob3VyIGFuZCBkYXkgb2YgdGhlIHdlZWsKaGVhdC5EYXlIb3VyLnN0ZXBzIDwtIHNjaUF2Z3MuRGF5SG91ciAlPiUgZ2dwbG90KGFlcyh4ID0gaG91ciwgeT1EYXksIGZpbGw9YXZnU3RlcFRvdGFsKSkgKwogIGdlb21fdGlsZSgpICsKICBnZ3RpdGxlKCJPbiBBdmVyYWdlLCBNb3JlIFN0ZXBzIG9uIFdlZG5lc2RheXMgNS02IHAubS4sIFNhdHVyZGF5IDEtMiBwLm0uLCBhbmQgVHVlc2RheXMgYXQgTm9vbiIpICsKICBsYWJzKGZpbGwgPSAiYXZlcmFnZSBzdGVwcyIsIHN1YnRpdGxlPSJNb3JlIGludGVuc2Ugd29ya291dHMgYWZ0ZXIgNSBwLm0uIG9uIGVhcmxpZXIgd2Vla2RheXMgb2YgdGhlIHdlZWsgKE1vbi4tV2VkLikiLCBjYXB0aW9uID0gIkZpdGJpdCBkYXRhIGZvciB0aHJlZSB3ZWVrcyAoU3VuLCBBcHJpbCAxNyB0byBTYXQsIE1heSAwNywgMjAxNikgXG5Tb3VyY2U6IEZ1cmJlcmcgZXQgYWwuIDIwMTYuIFplbm9kby4iLCB5PSJkYXkiKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHM9IG1vdmluZ0hycykgKwogIHNjYWxlX3lfZGlzY3JldGUobGltaXRzPSByZXYobGkuZGF5c29md2spKSArCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIocGFsZXR0ZT0iR25CdSIsIGRpcmVjdGlvbiA9MSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGxvdC5jYXB0aW9uLnBvc2l0aW9uID0gInBsb3QiLAogICAgICAgIHBsb3QudGl0bGUucG9zaXRpb249InBsb3QiLAogICAgICAgIHBsb3QudGl0bGU9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpCmhlYXQuRGF5SG91ci5zdGVwcwoKIyBnZ3NhdmUoIi4vcGxvdHMvaGVhdC5EYXlIb3VyLnN0ZXBzLnBuZyIgLCB3aWR0aCA9IDEwLjUgLCBoZWlnaHQgPSA3LCBkcGk9MzAwKQoKIyBzZXBhcmF0ZSBkYXRhIGJ5IHVzZXJUeXBlIAphdmdzQnlIb3Vyc19MTSA8LSBzY2lfZmlsICU+JSBmaWx0ZXIodXNlcl90eXBlID09ICJMTSIpICU+JSBmbi5ob3VyRGF5LmF2Z3MoKQojVmlldyhhdmdzQnlIb3Vyc19MTSkKCmF2Z3NCeUhvdXJzX0xNViA8LSBzY2lfZmlsICU+JSBmaWx0ZXIodXNlcl90eXBlID09ICJMTVYiKSAlPiUgZm4uaG91ckRheS5hdmdzKCkKI1ZpZXcoYXZnc0J5SG91cnNfTE1WKQoKCiMgZm9yIGZyZXF1ZW50IGxpZ2h0LW1vZGVyYXRlIGV4ZXJjaXNlcnMgb25seSAtIGF2ZXJhZ2UgdG90YWwgaW50ZW5zaXRpZXMgYnkgaG91ciBhbmQgZGF5IG9mIHdlZWsKaGVhdC5MTS5pbnRlbnNpdHkgPC0gYXZnc0J5SG91cnNfTE0gJT4lIAogIGdncGxvdChhZXMoeCA9IGhvdXIsIHkgPSBEYXksIGZpbGw9YXZnVG90YWxJbnRlbnNpdHkpKSArCiAgZ2VvbV90aWxlKCkgKwogIGdndGl0bGUoIk9uIEF2ZXJhZ2UgZm9yIExpZ2h0L01vZGVyYXRlIEV4ZXJjaXNlcnM6IE1vcmUgSW50ZW5zZSBBY3Rpdml0eSBvbiBXZWVrZW5kcywgYW5kIFxuV2Vla2RheXMgQWZ0ZXIgNiBwLm0uIikgKwogIGxhYnMoZmlsbCA9ICJhdmVyYWdlIGludGVuc2l0eSIsIHN1YnRpdGxlPSJNb25kYXksIFR1ZXNkYXksIGFuZCBXZWRuZXNkYXkgc2hvdyBtb3JlIGludGVuc2UgYWN0aXZpdHksIG9mIHRoZSB3ZWVrZGF5cyIsIGNhcHRpb24gPSAiRml0Yml0IGRhdGEgZm9yIHRocmVlIHdlZWtzIChTdW4sIEFwcmlsIDE3IHRvIFNhdCwgTWF5IDA3LCAyMDE2KSBcblNvdXJjZTogRnVyYmVyZyBldCBhbC4gMjAxNi4gWmVub2RvLiIsIHk9ImRheSIpICsKICBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cz0gcmV2KGxpLmRheXNvZndrKSkgKwogIHNjYWxlX3hfZGlzY3JldGUobGltaXRzPW1vdmluZ0hycykgKwogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKHBhbGV0dGU9IllsR24iLCBkaXJlY3Rpb24gPTEpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBsb3QuY2FwdGlvbi5wb3NpdGlvbiA9ICJwbG90IiwKICAgICAgICBwbG90LnRpdGxlLnBvc2l0aW9uID0gInBsb3QiLAogICAgICAgIHBsb3QudGl0bGU9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpCmhlYXQuTE0uaW50ZW5zaXR5CgojIGdnc2F2ZSgiLi9wbG90cy9oZWF0LkxNLmludGVuc2l0eS5wbmciLCB3aWR0aCA9IDEwLjUgLCBoZWlnaHQgPSA3LCBkcGk9MzAwKQoKIyBmcmVxdWVudCBsaWdodC1tb2RlcmF0ZS12ZXJ5IGFjdGl2ZSBleGVyY2lzZXJzIC0gYXZlcmFnZSB0b3RhbCBpbnRlbnNpdGllcyBieSBob3VyIGFuZCBkYXkgb2Ygd2VlawpoZWF0LkxNVi5pbnRlbnNpdHkgPC0gYXZnc0J5SG91cnNfTE1WICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBob3VyLCB5ID0gRGF5LCBmaWxsPWF2Z1RvdGFsSW50ZW5zaXR5KSkgKwogIGdlb21fdGlsZSgpICsKICBnZ3RpdGxlKCJPbiBBdmVyYWdlIGZvciBWZXJ5IEFjdGl2ZSBFeGVyY2lzZXJzOiBNb3JlIEFjdGl2aXR5IEFmdGVyIDUgcC5tLiwgTW9uZGF5IHRocm91Z2ggV2VkbmVzZGF5IikgKwogIGxhYnMoZmlsbCA9ICJhdmVyYWdlIGludGVuc2l0eSIsIHN1YnRpdGxlPSJNb25kYXksIFR1ZXNkYXksIGFuZCBTYXR1cmRheSBob3VycyBhcm91bmQgbm9vbiBhbmQgU3VuZGF5IG5pZ2h0cyBhbHNvIGhhdmUgbW9yZSBpbnRlbnNlIGFjdGl2aXR5IiwgY2FwdGlvbiA9ICJGaXRiaXQgZGF0YSBmb3IgdGhyZWUgd2Vla3MgKFN1biwgQXByaWwgMTcgdG8gU2F0LCBNYXkgMDcsIDIwMTYpIFxuU291cmNlOiBGdXJiZXJnIGV0IGFsLiAyMDE2LiBaZW5vZG8uIiwgeT0iZGF5IikgKwogIHNjYWxlX3lfZGlzY3JldGUobGltaXRzPSByZXYobGkuZGF5c29md2spKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHM9bW92aW5nSHJzKSArCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIocGFsZXR0ZT0iT3JSZCIsIGRpcmVjdGlvbiA9MSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGxvdC5jYXB0aW9uLnBvc2l0aW9uID0gInBsb3QiLAogICAgICAgIHBsb3QudGl0bGUucG9zaXRpb24gPSAicGxvdCIsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpCmhlYXQuTE1WLmludGVuc2l0eQoKIyBnZ3NhdmUoIi4vcGxvdHMvaGVhdC5MTVYuaW50ZW5zaXR5LnBuZyIgLCB3aWR0aCA9IDEwLjUgLCBoZWlnaHQgPSA3LCBkcGk9MzAwKQoKCmBgYAoKIyMjIyBLZXkgRmluZGluZ3MgLS0gSG91cmx5IEFjdGl2aXR5CgpCYXNlZCBvbiB0aGUgRml0Yml0IGhvdXJseSBleGVyY2lzZSBoZWF0bWFwcywgSSB3b3VsZCBzdWdnZXN0IHRoZSBmb2xsb3dpbmcgdG8gaW1wcm92ZSBCZWxsYWJlYXQgVGltZToKCiogUHJvdmlkZSBhIHF1ZXN0aW9ubmFpcmUgZm9yIHVzZXJzIGluIHRoZSB0cmFja2luZyBhcHAgdG8gYXNrIHdoYXQga2luZCBvZiBleGVyY2lzZSBpbnRlbnNpdHkgdGhleSBpbnRlbmQgb24gZG9pbmcgb24gYSB3ZWVrbHkgYmFzaXMgKHZlcnksIG1vZGVyYXRlLCBvciBsaWdodCBhY3Rpdml0eSkgYW5kIHN1Z2dlc3Qgb3B0aW1hbCBleGVyY2lzZSBob3VycyBiYXNlZCBvbiBob3VybHkgdHJlbmRzIG9mIEZpdGJpdCB1c2VycywgZ3JvdXBlZCBieSBleGVyY2lzZSBpbnRlbnNpdHkgdHlwZS4KCiogQXMgd2Vla2RheXMgYWZ0ZXIgNSBwLm0uIGFuZCB3ZWVrZW5kIGFmdGVybm9vbnMgYXJlIG9uIGF2ZXJhZ2UgbW9yZSBwb3B1bGFyIGludGVuc2UgYWN0aXZpdHkgd29ya291dCB0aW1lcywgZ2l2aW5nIHVzZXJzIHRoZSBvcHRpb24gdG8gY3JlYXRlIGFuIGhvdXJseSBleGVyY2lzZSBzY2hlZHVsZSwgaW5jbHVkaW5nIHNldHRpbmcgcmVtaW5kZXJzIHRvIGV4ZXJjaXNlIGF0IGNlcnRhaW4gdGltZXMgb2YgdGhlIGRheSBjb3VsZCBpbXByb3ZlIGNvbnNpc3RlbmN5IG9mIHdvcmtvdXRzLgoKCgojIyBTbGVlcCBUcmFja2luZwojIyMjIFByZXBhcmUgLS0gQ2xlYW4gc2xlZXAgdHJhY2tpbmcgZGF0YQoKKioxLiBDbGVhbmluZyB0aGUgc2xlZXAgZGF0YSoqClRoZSBvcmlnaW5hbCBzbGVlcERheSBvbmx5IGhhcyBkYXRhIGZvciAyNCB1c2Vycy4gVGhpcyBzbWFsbCBzYW1wbGUgc2l6ZSwgYmVpbmcgPCAzMCwgaXMgYSBsaW1pdGF0aW9uIG9mIHRoZSBkYXRhIGFuYWx5c2lzLgoKRmlyc3QsIEkgd2FudGVkIHRvIHNlZSBob3cgY29uc2lzdGVudGx5IGVhY2ggdXNlciBsb2dnZWQgdGhlaXIgc2xlZXAgbWludXRlcyBvdmVyIGEgcGVyaW9kIG9mIHRpbWUuIEFnYWluLCBJIHBlcmZvcm1lZCB0aGUgdXN1YWwgcHJlcGFyYXRpb24gc3RlcHMsIGFzc2lnbmluZyBJZDIgdG8gZWFjaCBJZCBmb3IgZWFzaWVyIHJlYWRhYmlsaXR5IGFuZCBqb2luaW5nIHRoZSBkYWlseSBhY3Rpdml0eSBkYXRhIHdpdGggdGhlIHNsZWVwIGRhdGEuCgpXaGVuIGNsZWFuaW5nIHRoZSBkYXRhLCBJIGZvdW5kIHRocmVlIHVzZXJzIHdpdGggZHVwbGljYXRlIGRhdGEgZm9yIHRoZSBzYW1lIGRhdGVzLiBJIHJlbW92ZWQgdGhlc2UgZHVwbGljYXRlIHJvd3MgYmVmb3JlIHBsb3R0aW5nIHRoZSBkYXRhLgoKCmBgYHtyIHNsZWVwLWFuYWx5c2lzfQoKIyBtZXJnZSBzbGVlcERheSBhbmQgZC5hY3Rpdml0eQojVmlldyhzbGVlcERheSkKI1ZpZXcoZC5hY3Rpdml0eSkKc2xlZXBEYXkkZGF0ZSA8LSBhcy5EYXRlKHNsZWVwRGF5JFNsZWVwRGF5LCBmb3JtYXQ9IiVtLyVkLyVZIikKc2xlZXBEYXkgPC0gbWVyZ2UoeD1zbGVlcERheSwgeT1JZC5uZXcsIGJ5PSJJZCIsIGFsbC54ID0gVFJVRSApCiNnbGltcHNlKHNsZWVwRGF5KSAjIDQxMyByb3dzCgpzbGVlcERheV9zZWwgPC0gc2xlZXBEYXkgJT4lIHNlbGVjdChUb3RhbFNsZWVwUmVjb3JkczogSWQyKSAKCnNsZWVwSWQgPC0gc2xlZXBEYXlfc2VsICU+JSBncm91cF9ieShJZDIpICU+JSBzdW1tYXJpemUobigpKSAlPiUgZ2xpbXBzZSgpCiNWaWV3KHNsZWVwSWQpCgpzbGVlcERhdGVzIDwtIHNsZWVwRGF5X3NlbCAlPiUgZ3JvdXBfYnkoZGF0ZSkgJT4lIHN1bW1hcml6ZShuKCkpICU+JSBnbGltcHNlKCkKI1ZpZXcoc2xlZXBEYXRlcykKCiMgY2hlY2sgc2xlZXAgZGF0YSBmb3IgY291bnQgYnkgZGF0ZSBhbmQgdXNlciBJZDIKc2xlZXBUZXN0IDwtIHNsZWVwRGF5X3NlbCAgJT4lIGdyb3VwX2J5KGRhdGUsIElkMikgJT4lIHN1bW1hcml6ZShjb3VudCA9IG4oKSkgJT4lIGFycmFuZ2UoZGVzYyhjb3VudCkpCiNWaWV3KHNsZWVwVGVzdCkKCiMjIElkMjogUCwgUywgWkQgZWFjaCBoYXZlIDIgZW50cmllcyBmb3Igc2FtZSBkYXRlIChkdXBsaWNhdGUgZGF0YSkKc2xlZXBUZXN0MiA8LSBzbGVlcERheV9zZWwgJT4lIGZpbHRlciggSWQyID09ICJaRCIgJiBkYXRlID09IGFzLkRhdGUoIjIwMTYtMDQtMjUiKSB8IElkMiA9PSAiUCIgJiBkYXRlID09IGFzLkRhdGUoIjIwMTYtMDUtMDUiKSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElkMiA9PSAiUyIgJiBkYXRlPT0gYXMuRGF0ZSgiMjAxNi0wNS0wNyIpKQojIFZpZXcoc2xlZXBUZXN0MikgCgojIHJlbW92ZSBkdXBsaWNhdGVkIHJvd3MgaW4gc2xlZXAgZGF0YQpzbGVlcERheV9zZWwgPC0gc2xlZXBEYXlfc2VsICU+JSBkaXN0aW5jdCgpICU+JSBnbGltcHNlKCkgIyA0MTAgcm93cwoKIyBtZXJnZSBzbGVlcCBhbmQgYWN0aXZpdHkgZGF0YQpzbGVlcEFjdGl2aXR5IDwtIG1lcmdlKHg9c2xlZXBEYXlfc2VsLCB5PWQuYWN0aXZpdHksIGJ5PWMoIklkMiIsICJkYXRlIiksIGFsbC54PVRSVUUpCgojIGFkZCBjb2x1bW5zIGZvciB0b3RhbCBtaW51dGVzIG9mIGV4ZXJjaXNlCnNsZWVwQWN0aXZpdHkgPC0gc2xlZXBBY3Rpdml0eSAlPiUgbXV0YXRlKExpZ2h0TW9kTWludXRlcyA9IChMaWdodGx5QWN0aXZlTWludXRlcyArIEZhaXJseUFjdGl2ZU1pbnV0ZXMpKSAlPiUgCiAgbXV0YXRlKFRvdGFsQWN0aXZlTWludXRlcyA9IChWZXJ5QWN0aXZlTWludXRlcyArIExpZ2h0TW9kTWludXRlcykpCgojVmlldyhzbGVlcEFjdGl2aXR5KQojIGFkZCBleGVyY2lzZSBpbnRlbnNpdHkgdHlwZSBvZiBlYWNoIHVzZXIKc2xlZXBBY3Rpdml0eS51c2VyVHlwZXMgPC0gbWVyZ2UoeCA9IHNsZWVwQWN0aXZpdHksIHkgPSB1c2VyVHlwZXMsIGJ5ID0gIklkMiIsIGFsbC54PVRSVUUpCgojKHNsZWVwQWN0aXZpdHkudXNlclR5cGVzKQoKYGBgCgoKIyMjIERhaWx5IFNsZWVwIFJlY29yZHMKIyMjIyBBbmFseXplIC0tIENvbnNpc3RlbmN5IG9mIHNsZWVwIHJlY29yZHMgcGVyIHVzZXIKCkFnYWluLCBJIGZpbHRlcmVkIHRoZSBkYWlseSBzbGVlcCBkYXRhIHRvIHRocmVlIHdlZWtzLiBJIHdhbnRlZCB0byBzZWUgaG93IGNvbnNpc3RlbnQgdXNlcnMgd2VyZSByZWNvcmRpbmcgdGhlaXIgc2xlZXAgdGltZSwgc28gSSBjb3VudGVkIHRoZSB0b3RhbCBudW1iZXIgb2YgZGF0ZSBlbnRyaWVzIGZvciBlYWNoIHVzZXIuCgpgYGB7ciBhbmFseXplIHNsZWVwIGFjdGl2aXR5fQojIGZpbHRlciBkYXRhIGZvciB0aHJlZSB3ZWVrIHBlcmlvZApzbGVlcEFjdGl2aXR5LnVzZXJUeXBlc19maWwgPC0gc2xlZXBBY3Rpdml0eS51c2VyVHlwZXMgJT4lIGZpbHRlcihiZXR3ZWVuKGRhdGUsIGFzLkRhdGUoIjIwMTYtMDQtMTciKSwgYXMuRGF0ZSgiMjAxNi0wNS0wNyIpKSkKCiNWaWV3KHNsZWVwQWN0aXZpdHkudXNlclR5cGVzX2ZpbCkKIyBIb3cgY29uc2lzdGVudCBpcyB0aGUgZGF0YT8KIyMgSG93IG1hbnkgZGF5cyBlYWNoIHVzZXIgbG9nZ2VkIHNsZWVwIGRhdGE/CnNsZWVwVXNlcnMgPC0gc2xlZXBBY3Rpdml0eS51c2VyVHlwZXNfZmlsICU+JSBncm91cF9ieShJZDIpICU+JSBzdW1tYXJpemUobnVtX2RheXM9bigpKSAlPiUgZ2xpbXBzZSgpICMgMjMgdXNlcnMKI1ZpZXcoc2xlZXBVc2VycykKCiMjIGNvdW50IGhvdyBtYW55IHVzZXJzIGxvZ2dlZCBob3cgbWFueSBkYXlzCnNsZWVwLmRhdGVDb3VudCA8LSBzbGVlcFVzZXJzICU+JSB1bmdyb3VwKCkgJT4lIGdyb3VwX2J5KG51bV9kYXlzKSAlPiUgc3VtbWFyaXplKHVzZXJfY291bnQ9bigpKQoKdXNlckNvdW50IDwtIHNsZWVwLmRhdGVDb3VudCAlPiUgc3VtbWFyaXplKHRvdGFsVXNlcnMgPSBzdW0odXNlcl9jb3VudCkpCiNnbGltcHNlKHVzZXJDb3VudCkKCmBgYAoKCiMjIyMgU2hhcmUgLS0gVmlzdWFsaXplIHVzZXIgcGVyY2VudGFnZSBieSBudW1iZXIgb2YgZGF5cwoKVGhlIGJhciBncmFwaCBzaG93cyB0aGUgcHJvcG9ydGlvbiBvZiAyMyB1c2VycyB0aGF0IGxvZ2dlZCB0aGVpciBzbGVlcCBkYXRhIGZvciBob3cgbWFueSBkYXlzIG9mIHRoZSBzZWxlY3RlZCB0aHJlZSB3ZWVrcy4gT25seSA0IHVzZXJzIChhcm91bmQgMTglKSBsb2dnZWQgc2xlZXAgbWludXRlcyBmb3IgYWxsIDIxIGRheXMuCmBgYHtyIHBsb3Qgc2xlZXBhbmFseXNpcywgZmlnLndpZHRoPTksIGZpZy53aWR0aD03fQojIHBsb3QgdG8gc2hvdyBwZXJjZW50YWdlIG9mIHVzZXJzIGFuZCByZWNvcmQgb2Ygc2xlZXAgZGF0YSBvdmVyIHRpbWUKCmJhci5zbGVlcC5kYXRlcyA8LSBzbGVlcC5kYXRlQ291bnQgJT4lIGdncGxvdChhZXMoeD1udW1fZGF5cywgeSA9IHVzZXJfY291bnQvIHN1bSh1c2VyX2NvdW50KSkpICsgCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBmaWxsID0gInNsYXRlZ3JheTIiKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHVzZXJfY291bnQpLCB2anVzdD0tLjI1KSArCiAgZ2d0aXRsZSgiRmV3ZXIgdGhhbiAyMCUgb2YgdXNlcnMgcmVjb3JkZWQgc2xlZXAgZGF0YSBkYWlseSBmb3IgMyB3ZWVrcyIpICsKICBsYWJzKCB4ID0gImRheXMiLCB5ID0gInVzZXJzICgyMyB0b3RhbCkiLCBzdWJ0aXRsZT0iRml0Yml0IHNsZWVwIHRyYWNraW5nIGRhdGEgZm9yIEFwcmlsIDE3IHRocm91Z2ggTWF5IDcsIDIwMTYiLCBjYXB0aW9uPSJCYXIgbGFiZWxzIGFyZSBudW1iZXIgb2YgdXNlcnMgXG5Tb3VyY2U6IEZ1cmJlcmcgZXQgYWwuIDIwMTYuIFplbm9kby4iKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMSwgMjEsIDIpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OmxhYmVsX3BlcmNlbnQoYWNjdXJhY3k9MSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsIDEpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiKSkKCmJhci5zbGVlcC5kYXRlcwoKIyBnZ3NhdmUoIi4vcGxvdHMvYmFyLnNsZWVwLmRhdGVzLnBuZyIgLCB3aWR0aCA9IDkgLCBoZWlnaHQgPSA3LCBkcGk9MzAwKQoKCmBgYAoKCgpCYXIgZ3JhcGggcmVmZXJlbmNlczoKCiogVGhvbWFzIE5laXRtYW5uJ3MgW2Jsb2dwb3N0IGFib3V0IHBlcmNlbnRhZ2Ugc2NhbGVzXShodHRwczovL3Rob21hc2FkdmVudHVyZS5ibG9nL3Bvc3RzL2dncGxvdDItcGVyY2VudGFnZS1zY2FsZS8pIHdhcyBhIGhlbHBmdWwgZXhwbGFpbmVyIG9uIGhvdyB0byB1c2UgdGhlIHNjYWxlcyBwYWNrYWdlLiBOb3RlIHRoYXQgcGVyY2VudCgpIGFuZCBwZXJjZW50X2Zvcm1hdCgpIGFyZSByZXRpcmVkLCBhY2NvcmRpbmcgdG8gdGhlIFtzY2FsZXMgcGFja2FnZSBkb2N1bWVudGF0aW9uXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvc2NhbGVzL3NjYWxlcy5wZGYpLCBzbyBJIHVzZWQgbGFiZWxfcGVyY2VudCgpIGluc3RlYWQuCgoqIFRvIGFkanVzdCB0aGUgeS1heGlzIHNjYWxlIGxpbWl0cyB0byBzaG93IDAgdG8gMTAwJSwgSSB1c2VkIGNvb3JkX2NhcnRlc2lhbigpLiBUbyByZWFkIG1vcmUgYWJvdXQgdGhpcywgc2VlIHJvZWxwaSdzIFtibG9nIHBvc3RdKGh0dHBzOi8vd3d3LnJvZWxwZXRlcnMuYmUvYWRkLXBlcmNlbnRhZ2VzLXRvLXlvdXItYXhlcy1pbi1ycy1nZ3Bsb3QyLWFuZC1zZXQtdGhlLWxpbWl0cy8pIGFib3V0IGFkZGluZyBwZXJjZW50YWdlIGxpbWl0cyB1c2luZyBnZ3Bsb3QyCgoqIEkgcmVmZXJyZWQgdG8gYSBbU3RhY2sgT3ZlcmZsb3cgZGlzY3Vzc2lvbl0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTIwMTg0OTkvaG93LXRvLXB1dC1sYWJlbHMtb3Zlci1nZW9tLWJhci1mb3ItZWFjaC1iYXItaW4tci13aXRoLWdncGxvdDIpIGZvciBhZGRpbmcgbGFiZWxzIHRvIGJhcnMgaW4gZ2dwbG90MiwgbmFtZWx5IHRoZSByZXNwb25zZSBmcm9tIHVzZXIgcmNzIHRvIGFkanVzdCB0aGUgcGxhY2VtZW50IHVzaW5nIHZqdXN0LgoKIyMjIyBLZXkgRmluZGluZ3MgLS0gU2xlZXAgUmVjb3JkcwoKVGhlIGxhY2sgb2YgY29uc2lzdGVudCBkYWlseSBzbGVlcCBsb2dnaW5nIGFtb25nIEZpdGJpdCB1c2VycyBzaG93cyBhIHByb2JsZW1hdGljIHRyZW5kLiBQZXJoYXBzIHVzZXJzIGFyZSBub3Qgd2VhcmluZyB0aGVpciB0cmFja2luZyB3YXRjaGVzIHdoaWxlIHRoZXkgc2xlZXAuCiogSSBzdWdnZXN0IHRoYXQgdGhlIEJlbGxhYmVhdCB0ZWFtIGNyZWF0ZSBhIHdlYXJhYmxlIHNsZWVwIHRyYWNraW5nIHJpbmcsIHdoaWNoIHNvbWUgY29tcGFuaWVzIGxpa2UgW091cmFdKGh0dHBzOi8vb3VyYXJpbmcuY29tKSBoYXZlIGRlc2lnbmVkLiBUaGlzIGNvdWxkIGFjdCBhcyBhIGNvbXBhbmlvbiB0byB0aGUgVGltZSB3YXRjaCwgYW5kIHRoZSBzbWFsbGVyIGRldmljZSBtaWdodCBlbmNvdXJhZ2UgbW9yZSBwZW9wbGUgdG8gd2VhciBhIHRyYWNrZXIgd2hlbiBzbGVlcGluZy4KKiBQcm92aWRpbmcgZGFpbHkgcmVtaW5kZXJzIHRvIHdlYXIgdGhlIHdhdGNoIHdoaWxlIHNsZWVwaW5nLCB0aHJvdWdoIHRoZSBCZWxsYWJlYXQgYXBwLCBjb3VsZCBoZWxwIHByb21vdGUgY29uc2lzdGVuY3kgb2Ygc2xlZXAgdHJhY2tpbmcgZGF0YSBjb2xsZWN0aW9uLgoKIyMjIEhlYWx0aHkgRGFpbHkgU2xlZXAKIyMjIyBBbmFseXplIC0tIFRhcmdldCBzbGVlcCBkYXkgcGVyY2VudGFnZXMgVlMuIHVzZXIgcGVyY2VudGFnZXMKKioyLiBIb3cgb2Z0ZW4gZG8gRml0Yml0IHVzZXJzIGdldCBlbm91Z2ggZGFpbHkgc2xlZXA/KioKCkFjY29yZGluZyB0byBhIDIwMTUgc3R1ZHksIFtXYXRzb24gZXQgYWxdKGh0dHA6Ly9keC5kb2kub3JnLzEwLjU2NjUvc2xlZXAuNDcxNiksIGNpdGVkIGJ5IHRoZSBbQ0RDXShodHRwczovL3d3dy5jZGMuZ292L3NsZWVwL2Fib3V0X3NsZWVwL2hvd19tdWNoX3NsZWVwLmh0bWwpLCB0aGUgaGVhbHRoeSBhbW91bnQgb2Ygc2xlZXAgZm9yIGFuIGFkdWx0IGlzIDcgaG91cnMgb3IgbW9yZSBhIG5pZ2h0LiBTaW5jZSB0aGUgc2xlZXAgZGF0YSBpcyBpbiBtaW51dGVzLCB0aGlzIGNvbnZlcnRzIHRvIDQyMCsgbWludXRlcyBhIG5pZ2h0LgoKSSBjYXRlZ29yaXplZCBlYWNoIGRheSBhcyA+PSA0MjAgbWluLiBvciA8IDQyMCBtaW4uIHRvIGRldGVybWluZSBlYWNoIHVzZXIncyBwZXJjZW50YWdlIG9mIHRvdGFsIGRheXMgb2Ygc2xlZXAgcmVjb3Jkcy4gQnkgZ3JvdXBpbmcgdGhlIGRhdGEgZnVydGhlciwgSSBmb3VuZCB0aGUgdG90YWwgY291bnQgb2YgdXNlcnMgYW5kIHRoZSBwZXJjZW50YWdlIG9mIGhlYWx0aHkgZGF5cyBvZiB0aGUgc2xlZXAgZGF5cyByZWNvcmRlZCBwZXIgdXNlci4KCk5leHQsIEkgZ3JvdXBlZCB0aGUgcGVyY2VudGFnZSBvZiBoZWFsdGh5IHNsZWVwIGRhdGEgaW50byBwZXJjZW50IHJhbmdlcyBieSBxdWFydGVycyBhbmQgcmVjb3VudGVkIHRoZSB0b3RhbCB1c2VycyBwZXIgcGVyY2VudCByYW5nZSBhbmQgdGhlIHJlc3BlY3RpdmUgdXNlciBwZXJjZW50YWdlcy4gVGhpcyBkYXRhIGlzIGVhc2llciB0byB2aXN1YWxpemUgdGhhbiBpbmRpdmlkdWFsIG5vbi1ncm91cGVkIGhlYWx0aHkgc2xlZXAgZGF5IHBlcmNlbnRhZ2VzLgoKYGBge3Igc2xlZXAgYW5kIGV4ZXJjaXNlfQoKIyBGb3IgZWFjaCB1c2VyLCBob3cgb2Z0ZW4gZGlkIHRoZXkgZ2V0IGEgaGVhbHRoeSBhbW91bnQgb2Ygc2xlZXA/IAojVmlldyhzbGVlcEFjdGl2aXR5LnVzZXJUeXBlc19maWwpCgpoZWFsdGh5LnNsZWVwRGF5cyA8LSBzbGVlcEFjdGl2aXR5LnVzZXJUeXBlc19maWwgJT4lCiAgbXV0YXRlKHNsZWVwVGFyZ2V0ID0gY2FzZV93aGVuKFRvdGFsTWludXRlc0FzbGVlcCA+PSA0MjAgfiAiaGVhbHRoeSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRvdGFsTWludXRlc0FzbGVlcCA8IDQyMCB+ICJubyIpKQoKIyMgcGVyY2VudGFnZXMgYnkgbnVtYmVyIG9mIGRheXMgdGhleSByZWNvcmRlZCBzbGVlcCBkYXRhCmhlYWx0aHkuc2xlZXBEYXlzLmJ5VXNlciA8LSBoZWFsdGh5LnNsZWVwRGF5cyAlPiUgZ3JvdXBfYnkoSWQyKSAlPiUgCiAgc3VtbWFyaXplKGhlYWx0aHlfc2xlZXBfZGF5cyA9IHN1bShzbGVlcFRhcmdldCA9PSAiaGVhbHRoeSIpLAogICAgICAgICAgICB0b3RhbF9kYXlzID0gbigpKSAlPiUKICBtdXRhdGUocGN0X2hlYWx0aHlfc2xlZXAgPSByb3VuZChoZWFsdGh5X3NsZWVwX2RheXMgLyB0b3RhbF9kYXlzLCBkaWdpdHM9MikpCgojVmlldyhoZWFsdGh5LnNsZWVwRGF5cy5ieVVzZXIpCgojIyBncm91cCBieSBwZXJjZW50YWdlcyBvZiBoZWFsdGh5IHNsZWVwIGRheXMgdG8gZmluZCB1c2VyIGNvdW50cyBhbmQgcGVyY2VudGFnZXMKaGVhbHRoeS5zbGVlcERheXMucGN0VXNlcnMgPC0gaGVhbHRoeS5zbGVlcERheXMuYnlVc2VyICU+JSBncm91cF9ieShwY3RfaGVhbHRoeV9zbGVlcCkgJT4lIAogIHN1bW1hcml6ZShjb3VudF91c2VyID0gbigpKSAlPiUgCiAgbXV0YXRlKHBjdF91c2VyID0gcm91bmQoIGNvdW50X3VzZXIgLyBzdW0oY291bnRfdXNlciksIGRpZ2l0cz0yKSkgJT4lIHVuZ3JvdXAoKQpnbGltcHNlKGhlYWx0aHkuc2xlZXBEYXlzLnBjdFVzZXJzKQoKc3VtKGhlYWx0aHkuc2xlZXBEYXlzLnBjdFVzZXJzJGNvdW50X3VzZXIpICMyMyB0b3RhbCB1c2VycwoKCnJhbmdlcy5oZWFsdGh5U2xlZXBEYXlzLnBjdFVzZXJzIDwtIGhlYWx0aHkuc2xlZXBEYXlzLnBjdFVzZXJzICU+JSAKICBtdXRhdGUoIHJhbmdlc19zbGVlcERheXMgPSBkcGx5cjo6Y2FzZV93aGVuKCBwY3RfaGVhbHRoeV9zbGVlcCA+PSAwLjc1IH4gIjc1LTEwMCUiLAogICAgICAgICAgICAgICAgICAgIHBjdF9oZWFsdGh5X3NsZWVwID49IDAuNTAgfiAiNTAtNzQlIiwKICAgICAgICAgICAgICAgICAgICAgcGN0X2hlYWx0aHlfc2xlZXAgPj0gMC4yNSB+ICIyNS00OSUiLAogICAgICAgICAgICAgICAgICAgIHBjdF9oZWFsdGh5X3NsZWVwID4gMCB+ICIwLTI0JSIpKQoKIyByZXBsYWNlIE5BIHdpdGggMC0yNCUKcmFuZ2VzLmhlYWx0aHlTbGVlcERheXMucGN0VXNlcnNbaXMubmEocmFuZ2VzLmhlYWx0aHlTbGVlcERheXMucGN0VXNlcnMpXSA9IGMoIjAtMjQlIikgICAgICAgCgpyYW5nZXMuaGVhbHRoeVNsZWVwRGF5cy5wY3RVc2VycyA8LSByYW5nZXMuaGVhbHRoeVNsZWVwRGF5cy5wY3RVc2VycyAlPiUgCiAgc2VsZWN0KCFwY3RfdXNlcikgJT4lIAogIGdyb3VwX2J5KHJhbmdlc19zbGVlcERheXMpICU+JSAKICBzdW1tYXJpemUoY291bnRfdXNlciA9IHN1bShjb3VudF91c2VyKSkgJT4lIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKHBjdF91c2VyID0gcm91bmQoIGNvdW50X3VzZXIgLyBzdW0oY291bnRfdXNlciksIGRpZ2l0cz0yKSkgJT4lIHVuZ3JvdXAoKQogIAojVmlldyhyYW5nZXMuaGVhbHRoeVNsZWVwRGF5cy5wY3RVc2VycykKICAgICAgICAgICAgCmBgYAoKCiMjIyMgU2hhcmUgLS0gVmlzdWFsaXplIHRhcmdldCBzbGVlcCBkYXkgcGVyY2VudGFnZXMgYnkgdXNlcnMKCkluIHRoZSBiYXIgY2hhcnQsIGZld2VyIHRoYW4gMjUlIG9mIEZpdGJpdCB1c2VycyAoNSB1c2VycykgcmVjb3JkZWQgYXQgbGVhc3QgNzUlIGRheXMgb2YgaGVhbHRoeSBzbGVlcCBkdXJhdGlvbiAoYXQgbGVhc3QgNyBob3VycyBhIGRheSksIHdoaWxlIGZld2VyIHRoYW4gMjUlIG9mIHVzZXJzICg2IHVzZXJzKSBoYWQgZmV3ZXIgdGhhbiBhIHF1YXJ0ZXIgZGF5cyBvZiBlbm91Z2ggc2xlZXAuIFRoaXMgc3VnZ2VzdHMgdGhhdCB1c2VycyBjb3VsZCB1c2UgbW9yZSBjb25zaXN0ZW5jeSBpbiB0aGUgYW1vdW50IG9mIHNsZWVwIHRoZXkgYXJlIGdldHRpbmcuCgpgYGB7ciBwZXJjZW50IHNsZWVwIGRheXN9CgpiYXIucGN0SGVhbHRoeVNsZWVwRGF5cyA8LSByYW5nZXMuaGVhbHRoeVNsZWVwRGF5cy5wY3RVc2VycyAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gcmFuZ2VzX3NsZWVwRGF5cywgeSA9IHBjdF91c2VyKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgZmlsbD0ibGlnaHRza3libHVlIikgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBjb3VudF91c2VyKSwgdmp1c3QgPSAxLjQpICsKICBnZ3RpdGxlKCJGZXdlciB0aGFuIDEvNCBGaXRiaXQgdXNlcnMgaGFkIGF0IGxlYXN0IFxuNzUlIG9mIGhlYWx0aHkgc2xlZXAgZGF5cyAoNysgaG91cnMvZGF5KSIpICsKICBsYWJzKHggPSAiZGF5cyBvZiBoZWFsdGh5IHNsZWVwIiwgeT0idXNlcnMgKDIzIHRvdGFsKSIsIHN1YnRpdGxlPSJPdmVyIDEvNCB1c2VycyBoYWQgZmV3ZXIgdGhhbiAxLzQgdG90YWwgaGVhbHRoeSBzbGVlcCBkYXlzIChBcHJpbCAxNyB0byBNYXkgMDcsIDIwMTYpIiwgY2FwdGlvbiA9ICJCYXIgbGFiZWxzIGFyZSBudW1iZXIgb2YgdXNlcnMgXG5Tb3VyY2U6IEZ1cmJlcmcgZXQgYWwuIDIwMTYuIFplbm9kby4iKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OmxhYmVsX3BlcmNlbnQoYWNjdXJhY3k9MSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGxvdC5jYXB0aW9uLnBvc2l0aW9uID0gInBsb3QiLAogICAgICAgIHBsb3QudGl0bGU9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwgMSkpCmJhci5wY3RIZWFsdGh5U2xlZXBEYXlzIAoKIyBnZ3NhdmUoIi4vcGxvdHMvYmFyLnBjdEhlYWx0aHlTbGVlcERheXMucG5nIiAsIHdpZHRoID0gMTAsIGhlaWdodCA9IDcsIGRwaT0zMDApCgoKCmBgYAoKCiMjIyMgS2V5IEZpbmRpbmdzIC0tIFN1Z2dlc3Rpb25zIGZvciBNb3JlIEhlYWx0aHkgU2xlZXAgRGF5cwoKQXMgbmVhcmx5IGhhbGYgb2YgdGhlIEZpdGJpdCB1c2VycyBoYWQgZmV3ZXIgdGhhbiA1MCUgaGVhbHRoeSBzbGVlcCBkYXlzIG9mIHRoZWlyIHRvdGFsIHNsZWVwIHJlY29yZHMsIGFub3RoZXIgc2xlZXAgZGF0YSB0cmVuZCBpcyB0aGF0IG1vc3QgcGVvcGxlIGFyZSBub3Qgc2xlZXBpbmcgdGhlIGhlYWx0aHkgcmVjb21tZW5kZWQgYW1vdW50IG9mIDcrIGhvdXJzIHBlciBkYXkuIAoKSSBzdWdnZXN0IHRoYXQgQmVsbGFiZWF0IGNyZWF0ZSBhIGZ1bmN0aW9uIGluIHRoZSBhcHAgZm9yIGEgdXNlciB0byBzZWxlY3QgdGFyZ2V0IHNsZWVwIGFuZCB3YWtpbmcgdGltZXMgdGhhdCByZWZsZWN0IGEgaGVhbHRoeSBhbW91bnQgb2YgZGFpbHkgc2xlZXAuIFRoZSBhZGRpdGlvbiBvZiBub3RpZmljYXRpb24gcmVtaW5kZXJzIGZvciB1c2VycyB0byBzbGVlcCBhdCBhIGNlcnRhaW4gdGltZSBjb3VsZCBoZWxwIGluY3JlYXNlIHRoZSBudW1iZXIgb2YgdXNlcnMgd2hvIGFyZSBnZXR0aW5nIGVub3VnaCBkYWlseSBzbGVlcC4KCgojIyMgU2xlZXAgVlMuIEV4ZXJjaXNlIEFjdGl2aXR5CiMjIyMgQW5hbHl6ZQoqKklmIGEgcGVyc29uLCBvbiBhdmVyYWdlLCBzbGVlcHMgZW5vdWdoIGluIGEgd2VlaywgZG9lcyB0aGF0IGNvcnJlbGF0ZSB3aXRoIHRoZW0gc3BlbmRpbmcgbW9yZSB0aW1lIGV4ZXJjaXNpbmcgZHVyaW5nIHRoYXQgc2FtZSB3ZWVrPyoqCgpJIHdhbnRlZCB0byBleGFtaW5lOgoKKiBBcmUgc2xlZXAgdGltZXMgYW5kIGFjdGl2aXR5IHRpbWVzIGNvcnJlbGF0ZWQ/CiogQXJlIHNsZWVwIHRpbWVzIGFuZCBjYWxvcmllcyBjb3JyZWxhdGVkPwoKVGFraW5nIHRoZSBtZXJnZWQgc2xlZXAgYW5kIGRhaWx5IGFjdGl2aXR5IGRhdGFzZXQsIEkgZ3JvdXBlZCB0aGUgZGF0YSBieSB1c2VyIGFuZCB3ZWVrLCBhbmQgZm91bmQgdGhlIGF2ZXJhZ2UgdmFsdWVzIGZvciB0b3RhbCBzbGVlcCBmb3IgZWFjaCB3ZWVrIGFuZCB0aGUgdG90YWwgd2Vla2x5IGFjdGl2aXR5IG1pbnV0ZXMsIHN0ZXBzLCBjYWxvcmllcywgYXMgd2VsbCBhcyB0aGUgbWVhbiB3ZWVrbHkgY2Fsb3JpZXMuCgpgYGB7ciBzbGVlcCB4IGFjdGl2aXR5fQojIElzIHRoZXJlIGEgY29ycmVsYXRpb24gb2YgaGVhbHRoeSBkYWlseSBzbGVlcCBhbmQgZXhlcmNpc2UgYWN0aXZpdHk/CndrLnNsZWVwQWN0aXZpdHkgPC0gc2xlZXBBY3Rpdml0eS51c2VyVHlwZXNfZmlsICU+JSB1bmdyb3VwKCkgJT4lIGdyb3VwX2J5KElkMiwgd2Vla19kYXRlKSAlPiUgCiAgc3VtbWFyaXplKGF2Z1NsZWVwTWluID0gbWVhbihUb3RhbE1pbnV0ZXNBc2xlZXApLAogICAgICAgICAgICBtZWRpYW5TbGVlcE1pbiA9IG1lZGlhbihUb3RhbE1pbnV0ZXNBc2xlZXApLAogICAgICAgICAgICB3a0FjdGl2ZU1pbiA9IHN1bShUb3RhbEFjdGl2ZU1pbnV0ZXMpLAogICAgICAgICAgICB3a0xpZ2h0TW9kTWluID0gc3VtKExpZ2h0TW9kTWludXRlcyksCiAgICAgICAgICAgIHdrVmVyeUFjdGl2ZU1pbiA9IHN1bShWZXJ5QWN0aXZlTWludXRlcyksCiAgICAgICAgICAgIHRvdGFsU3RlcHMgPSBzdW0oVG90YWxTdGVwcyksCiAgICAgICAgICAgIHRvdGFsQ2Fsb3JpZXMgPSBzdW0oQ2Fsb3JpZXMpLAogICAgICAgICAgICBhdmdDYWxvcmllcyA9IG1lYW4oQ2Fsb3JpZXMpKQoKd2suc2xlZXBBY3Rpdml0eSA8LSBtZXJnZSh4ID0gd2suc2xlZXBBY3Rpdml0eSwgeSA9IHVzZXJUeXBlcywgYnk9IklkMiIsIGFsbC54PSBUUlVFKQoKIyBWaWV3KHdrLnNsZWVwQWN0aXZpdHkpCgpgYGAKCgoKIyMjIyBTaGFyZQoKRm9yIHRoZSBmaXJzdCBzY2F0dGVyIHBsb3Qgc2hvd2luZyBhdmVyYWdlIHNsZWVwIHRpbWUgYW5kIHRvdGFsIHdlZWtseSBhY3Rpdml0eSB0aW1lLCBJIGNvbG9yZWQgdGhlIHBvaW50cyBieSB0eXBlIG9mIGV4ZXJjaXNlciwgdXNpbmcgdGhlIHVzZXIgaW50ZW5zaXR5IHR5cGVzIGZyb20gdGhlIGVhcmxpZXIgYWN0aXZpdHkgYW5hbHlzaXMuIFRoZSBzaXplIG9mIHRoZSBwb2ludHMgcmVmbGVjdHMgdGhlIHRvdGFsIHdlZWtseSBjYWxvcmllcy4gQm90aCBncm91cHMgb2YgZXhlcmNpc2VycyBzaG93IGEgc2xpZ2h0bHkgcG9zaXRpdmUgY29ycmVsYXRpb24gb2YgdG90YWwgd2Vla2x5IGV4ZXJjaXNlIHRpbWUgYW5kIHdlZWtseSBhdmVyYWdlIHNsZWVwIHRpbWUuCgpJbiB0aGUgc2Vjb25kIHNjYXR0ZXIgcGxvdCwgdG90YWwgd2Vla2x5IGNhbG9yaWVzIGlzIGFsc28gc2xpZ2h0bHkgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHdpdGggd2Vla2x5IGF2ZXJhZ2Ugc2xlZXAgdGltZSwgdGhvdWdoIHRoZXJlIGFyZSBvdXRsaWVyIGNhc2VzLgoKYGBge3IgcGxvdCBzbGVlcC1leGVyY2lzZX0KIyB3ZWVrbHkgYWN0aXZlIG1pbnV0ZXMgdnMuIGF2ZXJhZ2Ugc2xlZXAgbWludXRlcwpzY2F0dGVyLndrLnNsZWVwQWN0aXZlTWluIDwtIHdrLnNsZWVwQWN0aXZpdHkgJT4lIAogIGdncGxvdChhZXMoeCA9IGF2Z1NsZWVwTWluLCB5ID0gd2tBY3RpdmVNaW4pKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSB1c2VyX3R5cGUsIHNpemU9dG90YWxDYWxvcmllcykpICsKICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIiwgc2UgPSBGQUxTRSwgYWxwaGEgPSAwLjIsIGFlcyhjb2xvciA9IHVzZXJfdHlwZSkpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA0MjAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsKICBnZ3RpdGxlKCJXZWVrbHkgQXZlcmFnZSBTbGVlcCBhbmQgQWN0aXZpdHkgVGltZXMgQXJlIFBvc2l0aXZlbHkgQ29ycmVsYXRlZCIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDI4MDAwLCA0MDAwKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJwYWxlZ3JlZW4zIiwgInNpZW5uYTIiLCAibGlnaHRnb2xkZW5yb2QyIiksIGxhYmVscz0gYygibG93IG9yIG1vZGVyYXRlIiwgImxvdywgbW9kZXJhdGUsIG9yIHZlcnkiLCAibm90IGFjdGl2ZSIpKSArCiAgbGFicyhzdWJ0aXRsZSA9ICJXZWVrbHkgRml0Yml0IGRhdGEgZm9yIEFwcmlsIDE3IHRvIE1heSA3LCAyMDE2IiwgeCA9ICJhdmVyYWdlIHNsZWVwIG1pbnV0ZXMiLCB5ID0gInRvdGFsIHdlZWtseSBhY3RpdmUgbWludXRlcyIsIHNpemUgPSAidG90YWwgd2VlayBjYWxvcmllcyIsIGNvbG9yID0gInR5cGUgb2YgZXhlcmNpc2VyIiwgY2FwdGlvbiA9ICJEb3R0ZWQgbGluZSBpcyBoZWFsdGh5IHNsZWVwIHBlciBkYXkgXG5Tb3VyY2U6IEZ1cmJlcmcgZXQgYWwuIDIwMTYuIFplbm9kby4iKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwbG90LmNhcHRpb24ucG9zaXRpb24gPSAicGxvdCIsCiAgICAgICAgcGxvdC50aXRsZT0gZWxlbWVudF90ZXh0KGZhY2U9ImJvbGQiKSkKc2NhdHRlci53ay5zbGVlcEFjdGl2ZU1pbgoKIyBnZ3NhdmUoIi4vcGxvdHMvc2NhdHRlci53ay5zbGVlcEFjdGl2ZU1pbi5wbmciICwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNywgZHBpPTMwMCkKCiMgdG90YWwgd2Vla2x5IGNhbG9yaWVzIHZzLiBhdmVyYWdlIHNsZWVwIG1pbnV0ZXMKc2NhdHRlci53ay5zbGVlcENhbG9yaWVzIDwtIHdrLnNsZWVwQWN0aXZpdHkgJT4lIAogIGdncGxvdChhZXMoeCA9IGF2Z1NsZWVwTWluLCB5ID0gdG90YWxDYWxvcmllcykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJzbGF0ZWdyYXkyIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDQyMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKwogIGdndGl0bGUoIldlZWtseSBBdmVyYWdlIFNsZWVwIFRpbWUgYW5kIFRvdGFsIENhbG9yaWVzIEFyZSBQb3NpdGl2ZWx5IENvcnJlbGF0ZWQiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAyODAwMCwgNDAwMCkpICsKICBsYWJzKHN1YnRpdGxlID0gIldlZWtseSBGaXRiaXQgZGF0YSBmb3IgQXByaWwgMTcgdG8gTWF5IDcsIDIwMTYiLCB4ID0gImF2ZXJhZ2Ugc2xlZXAgbWludXRlcyIsIHkgPSAidG90YWwgd2Vla2x5IGNhbG9yaWVzIiwgY2FwdGlvbiA9ICJEb3R0ZWQgbGluZSBpcyBoZWFsdGh5IHNsZWVwIHBlciBkYXkgXG5Tb3VyY2U6IEZ1cmJlcmcgZXQgYWwuIDIwMTYuIFplbm9kby4iKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShwbG90LmNhcHRpb24ucG9zaXRpb249InBsb3QiLAogICAgICAgIHBsb3QudGl0bGU9IGVsZW1lbnRfdGV4dChmYWNlPSJib2xkIikpCnNjYXR0ZXIud2suc2xlZXBDYWxvcmllcwoKIyBnZ3NhdmUoIi4vcGxvdHMvc2NhdHRlci53ay5zbGVlcENhbG9yaWVzLnBuZyIgLCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA3LCBkcGk9MzAwKQoKYGBgCgojIyMjIEtleSBGaW5kaW5ncyAtLSBTbGVlcCBhbmQgZXhlcmNpc2UgYWN0aXZpdHkKCk1vcmUgc2xlZXAgaXMgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHdpdGggbW9yZSBleGVyY2lzZSBhbmQgY2Fsb3JpZSBidXJuaW5nLCBieSB3ZWVrLiBJZiBCZWxsYWJlYXQgVGltZSB1c2VycyBhcmUgcHJvdmlkZWQgZGFpbHkgcmVtaW5kZXJzIHRvIG1lZXQgc2xlZXAgdGFyZ2V0cyAoNysgaG91cnMgLyBkYXkpLCB0aGF0IGNvdWxkIGluIHR1cm4sIGltcHJvdmUgdGhlaXIgZXhlcmNpc2UgYWN0aXZpdHkuIAoKCiMjIFN1bW1hcnkgb2YgUHJvamVjdCBBbmFseXNpcwoKRm9yIHRoaXMgY2FzZSBzdHVkeSwgSSBhbmFseXplZCBGaXRiaXQgZGF0YSByZWxhdGVkIHRvICoqYWN0aXZpdHkqKiBhbmQgKipzbGVlcCoqIHRvIGV4YW1pbmUgdHJlbmRzIGFtb25nIGZpdG5lc3MgdHJhY2tpbmcgdXNlcnMsIHRvIHRoZW4gaW5mb3JtIGluc2lnaHRzIGZvciBpbXByb3ZpbmcgdGhlIEJlbGxhYmVhdCBUaW1lIGZpdG5lc3Mgd2F0Y2gsIHdoaWNoIGlzIGRlc2lnbmVkIHRvIHRyYWNrIHVzZXIgYWN0aXZpdHkgYW5kIHNsZWVwIHBhdHRlcm5zLCBhbW9uZyBvdGhlciBtZWFzdXJlcy4KCiMjIyBBY3Rpdml0eQpJIGFuYWx5emVkICpkYWlseSBhY3Rpdml0eSogZGF0YSByZWxhdGVkIHRvIGFjdGl2aXR5IGludGVuc2l0aWVzICh2ZXJ5LCBtb2RlcmF0ZSwgYW5kIGxpZ2h0bHkgYWN0aXZlKSwgY2Fsb3JpZXMsIGFuZCBzdGVwcy4gQnkgZGV0ZXJtaW5pbmcgd2hldGhlciBvciBub3QgdXNlcnMgYWNoaWV2ZWQgaGVhbHRoeSBsaWdodC9tb2RlcmF0ZSBvciB2ZXJ5IGFjdGl2ZSB0aW1lcyBwZXIgd2VlayBhbmQgaG93IGZyZXF1ZW50bHkgdGhleSBtZXQgdGhvc2UgZ29hbHMgKGEgbWFqb3JpdHkgb2Ygd2Vla3MgYmVpbmcgYXQgbGVhc3QgMiBvZiAzIHdlZWtzKSwgSSBmb3VuZCB0d28gbWFpbiBncm91cHMgb2YgZXhlcmNpc2VyczoKCiogTGlnaHQvbW9kZXJhdGUgZXhlcmNpc2VycyAoYnV0IG5vdCB2ZXJ5IGFjdGl2ZSkgLS0gTE0KKiBsaWdodC9tb2RlcmF0ZSBhbmQgdmVyeSBhY3RpdmUgZXhlcmNpc2VycyAtLSBMTVYKClRoZW4sIEkgbG9va2VkIGF0ICpob3VybHkgYWN0aXZpdHkqIGRhdGEsIHVzaW5nIHRoaXMgc2FtZSBncm91cGluZyBvZiBleGVyY2lzZXIgdHlwZXMuIEkgZXhhbWluZWQgd2hlbiBwZW9wbGUgbW9zdCBmcmVxdWVudGx5IGV4ZXJjaXNlZCwgYmFzZWQgb24gYXZlcmFnZSB0b3RhbCBpbnRlbnNpdGllcyBieSBob3VyIGFuZCBkYXkgb2YgdGhlIHdlZWsgYW5kIGRldGVybWluZWQgc2VwYXJhdGUgdHJlbmRzIGZvciBkaWZmZXJlbnQgdHlwZXMgb2YgZXhlcmNpc2VycywgYWxvbmcgd2l0aCB0cmVuZHMgZm9yIHRoZSBvdmVyYWxsIHVzZXJzLgoKKkFjdGl2aXR5IFRyZW5kcyoKCiogV2lkZXIgZGlzdHJpYnV0aW9uIG9mIGFtb3VudCBvZiBhY3Rpdml0eSBvbiB3ZWVrZW5kcyB0aGFuIHdlZWtkYXlzCiogT2YgdGhlIHdlZWtkYXlzLCBzbGlnaHRseSBncmVhdGVyIG1lZGlhbiBvZiBhY3Rpdml0eSBpbnRlbnNpdHkgb24gVHVlc2RheXMKKiBNb3JlIHdlZWtseSBjYWxvcmllcyBidXJuZWQgaW4gbGVzcyB0aW1lIGFuZCBzaG9ydGVyIGRpc3RhbmNlIGZvciB1c2VycyB3aG8gZnJlcXVlbnRseSBhcmUgdmVyeSBhY3RpdmUsIGFzIGNvbXBhcmVkIHRvIGV4ZXJjaXNlcnMgd2hvIGFyZSBvbmx5IGZyZXF1ZW50bHkgbGlnaHQvbW9kZXJhdGVseSBhY3RpdmUKKiBPdmVyYWxsLCBtb3JlIGludGVuc2Ugd29ya291dHMgb2NjdXIgb24gVHVlc2RheXMgYXQgbm9vbiwgU2F0dXJkYXlzIGF0IDEgcC5tLiwgYW5kIGFmdGVyIDUgcC5tLiBvbiB3ZWVrZGF5cwoqIEZyZXF1ZW50IGxpZ2h0L21vZGVyYXRlIGV4ZXJjaXNlcnMgdGVuZCB0byBiZSBtb3JlIGFjdGl2ZSBvbiB3ZWVrZW5kcywgYW5kIHdlZWtkYXlzIGFmdGVyIDYgcC5tLgoqIEZyZXF1ZW50IHZlcnkgYWN0aXZlIGV4ZXJjaXNlcnMgdGVuZCB0byBiZSBtb3JlIGFjdGl2ZSBvbiBNb25kYXksIFR1ZXNkYXksIGFuZCBTYXR1cmRheSBhcm91bmQgbm9vbjsgYW5kIFN1bmRheSB0aHJvdWdoIFdlZG5lc2RheSBhZnRlciA1IHAubS4KCiMjIyBTbGVlcAoKRm9yIHRoZSAqc2xlZXAgZGF5KiBkYXRhLCBJIGFuYWx5emVkIHRoZSBmb2xsb3dpbmcgYW5kIGZvdW5kIHRoZXNlICp0cmVuZHMqOgoKKiBIb3cgb2Z0ZW4gd2VyZSB1c2VycyByZWNvcmRpbmcgc2xlZXAgZGF0YT8KICArIEZld2VyIHRoYW4gMjAlIG9mIHVzZXJzIGNvbnNpc3RlbnRseSByZWNvcmRlZCBzbGVlcCBkYXRhIG92ZXIgdGhyZWUgd2Vla3MKCiogSG93IG9mdGVuIHdlcmUgdXNlcnMgZ2V0dGluZyBhIGhlYWx0aHkgYW1vdW50IG9mIHNsZWVwIGVhY2ggZGF5IChhdCBsZWFzdCA3IGhvdXJzKSwgb2YgdGhlIHRvdGFsIGRheXMgdGhleSByZWNvcmRlZCBzbGVlcCBkYXRhPwogICsgRmV3ZXIgdGhhbiBhIHF1YXJ0ZXIgb2YgdXNlcnMgaGFkIGF0IGxlYXN0IDc1JSBkYXlzIG9mIGhlYWx0aHkgc2xlZXAKICArIE92ZXIgYSBxdWFydGVyIG9mIHVzZXJzIGhhZCBvbmx5IGxlc3MgdGhhbiAyNSUgZGF5cyBvZiBoZWFsdGh5IHNsZWVwCgoqIEJ5IG1lcmdpbmcgdGhlIHNsZWVwIGFuZCBhY3Rpdml0eSBkYXRhIGFuZCB2aXN1YWxpemluZyB0b3RhbCB3ZWVrbHkgYWN0aXZlIG1pbnV0ZXMgYW5kIGF2ZXJhZ2Ugc2xlZXAgbWludXRlcyBwZXIgd2VlaywgdGhlcmUgaXMgYSBzbGlnaHRseSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHNsZWVwIGFuZCBhY3Rpdml0eSBmb3IgZXhlcmNpc2VycyBvZiBlaXRoZXIgaW50ZW5zaXR5IGdyb3VwIChMTVYgb3IgTE0pLgoKIyMjIERhdGEgTGltaXRhdGlvbnMKClRoZSBkYXRhIHVzZWQgZm9yIHRoaXMgYW5hbHlzaXMgd2FzIGxpbWl0ZWQgdG8gYSBzbWFsbCBzYW1wbGUgc2l6ZSAoYXJvdW5kIDMwIG9yIGZld2VyIHVzZXJzKS4gCgpJbiBwYXJ0aWN1bGFyLCB0aGUgc2xlZXAgZGF0YXNldCBoYWQgaW5mb3JtYXRpb24gZm9yIGZld2VyIHRoYW4gMzAgdXNlcnMuIFRoZSBzbGVlcCB0cmVuZHMgZm91bmQgZnJvbSB0aGlzIGRhdGEgYW5hbHlzaXMgYXJlIG5vdCBhIHNpZ25pZmljYW50IHJlcHJlc2VudGF0aW9uIG9mIGEgbGFyZ2VyIHBvcHVsYXRpb24sIGR1ZSB0byB0aGUgc21hbGwgc2FtcGxlIHBvcHVsYXRpb24gYW5kIG1pc3NpbmcgZGF0YS4KCkFzIHRoZSBkYXRhIGNvbGxlY3RlZCB3YXMgZm9yIGEgc2hvcnQgdGltZSBwZXJpb2QsIGJldHdlZW4gQXByaWwgYW5kIE1heSAyMDE2LCB0aGUgaGVhbHRoIGFjdGl2aXR5IGFuZCBzbGVlcCBwYXR0ZXJucyBhcmUgbGltaXRlZCB0byBhbiBvcHRpbWFsIHNlYXNvbiBvZiBleGVyY2lzZSAoc3ByaW5nL2ZhbGwsIGRlcGVuZGluZyBvbiB0aGUgaGVtaXNwaGVyZSBsb2NhdGlvbiBvZiBlYWNoIHVzZXIpLiBBY3Rpdml0eSBhbmQgc2xlZXAgdHJlbmRzIGNvdWxkIHZhcnkgZHVyaW5nIG90aGVyIHNlYXNvbnMgb2YgdGhlIHllYXIuCgpEdWUgdG8gdGhlIGFub255bWl0eSBvZiB1c2VyIElkcywgbm8gaW5mb3JtYXRpb24gd2FzIGtub3duIGFib3V0IHVzZXJzJyBiaW9sb2dpY2FsIHNleCBvciBhZ2UsIG5vciB3b3JrIGhvdXJzLCBsaXZpbmcgY29uZGl0aW9ucywgYW5kIGNsYXNzIHN0YXR1cywgY2hhcmFjdGVyaXN0aWNzIHdoaWNoIGNvdWxkIGltcGFjdCBhIHBlcnNvbidzIGV4ZXJjaXNlIGFuZCBzbGVlcCBzY2hlZHVsZXMuCgoKIyMjIEFjdCAtLSBIaWdoLWxldmVsIFJlY29tbWVuZGF0aW9ucyBmb3IgU3Rha2Vob2xkZXJzCgpUaGUgZm9sbG93aW5nIHJlY29tbWVuZGF0aW9ucyBhcmUgYmFzZWQgb24gdHJlbmRzIGZyb20gdGhlIGFuYWx5c2lzIGFuZCBjYW4gYmUgYXBwbGllZCB0byBCZWxsYWJlYXQncyBmaXRuZXNzIHRyYWNraW5nIHdhdGNoLCBUaW1lLgoKKipBY3Rpdml0eSB0cmFja2luZyBzdWdnZXN0aW9ucyoqCgoqIFByb3ZpZGUgYSBxdWVzdGlvbm5haXJlIGZvciB1c2VycyB0byBkZXRlcm1pbmUgdGhlIHR5cGUgb2YgaW50ZW5zaXR5IHdvcmtvdXRzIHRoZXkgcGxhbiBvbiBkb2luZyByZWd1bGFybHkgYW5kIGFuIG9wdGlvbiB0byBzZXQgd2Vla2x5IGV4ZXJjaXNlIGdvYWxzCiogUHJvdmlkZSBhIHdlZWtseSBzdW1tYXJ5IGdyYXBoIHRvIHNob3cgdGltZXMgb2YgYWN0aXZpdHkgaW50ZW5zaXR5IHBlciBkYXkgYW5kIHdlZWtseSB0b3RhbHMgcGVyIHR5cGUgb2YgaW50ZW5zaXR5IChsaWdodC9tb2RlcmF0ZSBhbmQgdmVyeSBhY3RpdmUpCiogSW5jbHVkZSBhIHJlbWluZGVyIG9mIG9wdGltYWwgaW50ZW5zaXR5IGV4ZXJjaXNlIGdvYWxzIGR1cmluZyB0aGUgd2VlayBhbmQgdGhlIGRpZmZlcmVuY2Ugb2YgdGltZSBuZWVkZWQgdG8gY29tcGxldGUgdGhhdCB3ZWVrbHkgZ29hbCwgYmFzZWQgb24gZXhlcmNpc2UgaW50ZW5zaXR5IHRpbWUgYWxyZWFkeSBzcGVudAoqIEdpdmUgdXNlcnMgdGhlIG9wdGlvbiB0byBjcmVhdGUgaG91cmx5IGV4ZXJjaXNlIHNjaGVkdWxlcyB3aXRoIHJlbWluZGVyIG5vdGlmaWNhdGlvbnMKKiBTdWdnZXN0IG9wdGltYWwgZXhlcmNpc2UgaG91cnMgYmFzZWQgb24gdGhlIHR5cGUgb2YgZXhlcmNpc2UgdGhleSBpbnRlbmQgb24gZG9pbmcsIGFzIGFuc3dlcmVkIGluIHRoZWlyIHF1ZXN0aW9ubmFpcmUKCioqU2xlZXAgdHJhY2tpbmcgc3VnZ2VzdGlvbnMqKgoKKiBDcmVhdGUgYW4gb3B0aW9uYWwgc2xlZXBpbmcgdHJhY2tpbmcgcmluZyB0byBhY3QgYXMgYSBjb21wYW5pb24gdG8gdGhlIFRpbWUgd2F0Y2guIFRoZSByaW5nIGlzIGEgc21hbGxlciB3ZWFyYWJsZSBvcHRpb24gdGhhdCBjb3VsZCBlbmNvdXJhZ2UgdXNlcnMgdG8gd2VhciBhIHRyYWNraW5nIGRldmljZSBkYWlseSB3aGVuIHNsZWVwaW5nLgoqIFByb3ZpZGUgdGhlIG9wdGlvbiBvZiBkYWlseSBub3RpZmljYXRpb25zIHRvIHJlbWluZCB1c2VycyB0byB3ZWFyIHRoZSB3YXRjaCB3aGlsZSBzbGVlcGluZwoqIEFsbG93IHVzZXJzIHRvIHNlbGVjdCBvcHRpbWFsIHNsZWVwIGFuZCB3YWtpbmcgdGltZXMgYW5kIHNldCByZW1pbmRlcnMgdG8gZ28gdG8gYmVkIGJ5IHRoYXQgY2VydGFpbiB0aW1lCiogSW5mb3JtIHVzZXJzIG9mIHRoZSBiZW5lZml0cyBvZiBoZWFsdGh5IGFtb3VudHMgb2Ygc2xlZXAsIGNvcnJlbGF0ZWQgd2l0aCBtb3JlIHdlZWtseSBleGVyY2lzZQoKCiMjIFJlZmVyZW5jZXMKClRoZSBmb2xsb3dpbmcgcmVmZXJlbmNlcyBhcmUgb3JnYW5pemVkIGludG8gc2VjdGlvbnMgYnkgb3JkZXIgb2YgYXBwZWFyYW5jZS4KCiMjIyMgRGF0YSwgQmFja2dyb3VuZCBJbmZvcm1hdGlvbgoKMS4gRnVyYmVyZywgUi4sIEJyaW50b24sIEouLCBLZWF0aW5nLCBNLiwgJiBPcnRpeiwgQS4gKDIwMTYpLiAqQ3Jvd2Qtc291cmNlZCBGaXRiaXQgZGF0YXNldHMgMDMuMTIuMjAxNi0wNS4xMi4yMDE2Ki4gW0RhdGEgc2V0XS4gWmVub2RvLiBodHRwczovL2RvaS5vcmcvMTAuNTI4MS96ZW5vZG8uNTM4OTQuIFJldHJpZXZlZCBmcm9tIGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vYXJhc2huaWMvZml0Yml0LgoyLiBGaXRhYmFzZS4gKDIwMTgpLiAqRml0YWJhc2UgRGF0YSBEaWN0aW9uYXJ5Ki4gUmV0cmlldmVkIGZyb20gaHR0cHM6Ly93d3cuZml0YWJhc2UuY29tL21lZGlhLzE5MzAvZml0YWJhc2VkYXRhZGljdGlvbmFyeTEwMjMyMC5wZGYKMy4gTmF0aW9uYWwgSGVhbHRoIEludGVydmlldyBTdXJ2ZXkgKE5ISVMpLCBDZW50ZXJzIGZvciBEaXNlYXNlIENvbnRyb2wgYW5kIFByZXZlbnRpb24gKENEQykgLyBOYXRpb25hbCBDZW50ZXIgZm9yIEhlYWx0aCBTdGF0aXN0aWNzIChOQ0hTKS4gKDIwMjIpLiAqUGh5c2ljYWwgQWN0aXZpdHkuKiBSZXRyaWV2ZWQgZnJvbSBodHRwczovL3d3dy5oZWFsdGh5cGVvcGxlLmdvdi8yMDIwL3RvcGljcy1vYmplY3RpdmVzL3RvcGljL3BoeXNpY2FsLWFjdGl2aXR5L25hdGlvbmFsLXNuYXBzaG90Lgo0LiBDREMuICgyMDIyKSAqUGh5c2ljYWwgQWN0aXZpdHkqLiBSZXRyaWV2ZWQgZnJvbSBodHRwczovL3d3dy5jZGMuZ292L3BoeXNpY2FsYWN0aXZpdHkvYmFzaWNzL2FkdWx0cy9pbmRleC5odG0uCjUuIFdhdHNvbiBORiwgQmFkciBNUywgQmVsZW5reSBHLCBldCBhbC4gUmVjb21tZW5kZWQgYW1vdW50IG9mIHNsZWVwIGZvciBhIGhlYWx0aHkgYWR1bHQ6IGEgam9pbnQgY29uc2Vuc3VzIHN0YXRlbWVudCBvZiB0aGUgQW1lcmljYW4gQWNhZGVteSBvZiBTbGVlcCBNZWRpY2luZSBhbmQgU2xlZXAgUmVzZWFyY2ggU29jaWV0eS4gU2xlZXAuIDIwMTU7MzgoNik6ODQz4oCTODQ0LiBodHRwOi8vZHguZG9pLm9yZy8xMC41NjY1L3NsZWVwLjQ3MTYuCgoKIyMjIyBDb2RpbmcgUmVzb3VyY2VzCgoxLiBTb25pLCBZYXNoLiAoMjAxOCkgKkhvdyBJIGFuYWx5emVkIHRoZSBkYXRhIGZyb20gbXkgRml0Yml0IHRvIGltcHJvdmUgbXkgb3ZlcmFsbCBoZWFsdGguIGZyZWVDb2RlQ2FtcCouIGh0dHBzOi8vd3d3LmZyZWVjb2RlY2FtcC5vcmcvbmV3cy9ob3ctaS1hbmFseXplZC10aGUtZGF0YS1mcm9tLW15LWZpdGJpdC10by1pbXByb3ZlLW15LW92ZXJhbGwtaGVhbHRoLWEyZTM2NDI2ZDhmOS8uCjIuIEthc3NhbWJhcmEsIEFsYm91a2FkZWwuICpnZ2NvcnJwbG90OiBWaXN1YWxpemF0aW9uIG9mIGEgY29ycmVsYXRpb24gbWF0cml4IHVzaW5nIGdncGxvdDIqICgwLjEuMykuIGh0dHBzOi8vcnBrZ3MuZGF0YW5vdmlhLmNvbS9nZ2NvcnJwbG90Ly4KMy4gSG9sdHosIFlhbi4gKDIwMTgpICpWaW9saW4gcGxvdCB3aXRoIGluY2x1ZGVkIGJveHBsb3QgYW5kIHNhbXBsZSBzaXplIGluIGdncGxvdDIqLiBUaGUgUiBHcmFwaCBHYWxsZXJ5LiBodHRwczovL3d3dy5yLWdyYXBoLWdhbGxlcnkuY29tL3Zpb2xpbl9hbmRfYm94cGxvdF9nZ3Bsb3QyLmh0bWwuCjQuIEthbXZhciwgWmhpYW4gTi4gKDIwMjEpICpQYWNrYWdlIGF3ZWVrOiBDb252ZXJ0IERhdGVzIHRvIEFyYml0cmFyeSBXZWVrIERlZmluaXRpb25zKigxLjAyKS4gUmV0cmlldmVkIGZyb20gaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2F3ZWVrL2F3ZWVrLnBkZi4KNS4gTGksIERlYW5uYS4gKDIwMjApICpCYXNpYyBSIEd1aWRlIGZvciBOU0MgU3RhdGlzdGljcywgQ2hhcHRlciAxOTogU2NhdHRlcnBsb3RzIGFuZCBCZXN0IEZpdCBMaW5lcyAtIFR3byBTZXRzKi4gKEJvb2tkb3duKSBodHRwczovL2Jvb2tkb3duLm9yZy9kbGkvcmd1aWRlL3NjYXR0ZXJwbG90cy1hbmQtYmVzdC1maXQtbGluZXMtdHdvLXNldHMuaHRtbC4KNi4gSG9sdHosIFlhbi4gKDIwMTgpLiAqZ2dwbG90MiBoZWF0bWFwKi4gVGhlIFIgR3JhcGggR2FsbGVyeS4gaHR0cHM6Ly93d3cuci1ncmFwaC1nYWxsZXJ5LmNvbS83OS1sZXZlbHBsb3Qtd2l0aC1nZ3Bsb3QyLmh0bWwuCjcuIEhvbHR6LCBZYW4uICgyMDE4KS4gKlIgQ29sb3IgQnJld2VyJ3MgUGFsZXR0ZXMqLiBodHRwczovL3d3dy5yLWdyYXBoLWdhbGxlcnkuY29tLzM4LXJjb2xvcmJyZXdlcnMtcGFsZXR0ZXMuaHRtbC4KOC4gTmVpdG1hbm4sIFRob21hcy4gKDIwMjApICpUcmFuc2Zvcm0gYSBnZ3Bsb3QyIEF4aXMgdG8gYSBQZXJjZW50YWdlIFNjYWxlKi4gaHR0cHM6Ly90aG9tYXNhZHZlbnR1cmUuYmxvZy9wb3N0cy9nZ3Bsb3QyLXBlcmNlbnRhZ2Utc2NhbGUvLgo5LiBXaWNraGFtLCBIYWRsZXkgYW5kIFNlaWRlbCwgRGFuYS4gKDIwMjApICpQYWNrYWdlIHNjYWxlczogU2NhbGUgRnVuY3Rpb25zIGZvciBWaXN1YWxpemF0aW9uKigxLjEuMSkuIGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9zY2FsZXMvc2NhbGVzLnBkZi4KMTAuIHJvZWxwZXRlcnMuYmUuICgyMDE5KSAqQWRkIHBlcmNlbnRhZ2VzIHRvIHlvdXIgYXhlcyBpbiBSJ3MgZ2dwbG90MiAoYW5kIHNldCB0aGUgbGltaXRzKSouIGh0dHBzOi8vd3d3LnJvZWxwZXRlcnMuYmUvYWRkLXBlcmNlbnRhZ2VzLXRvLXlvdXItYXhlcy1pbi1ycy1nZ3Bsb3QyLWFuZC1zZXQtdGhlLWxpbWl0cy8uCjExLiByY3MuICgyMDEyKSAqSG93IHRvIHB1dCBsYWJlbHMgb3ZlciBnZW9tX2JhciBmb3IgZWFjaCBiYXIgaW4gUiB3aXRoIGdncGxvdDIqLiBTdGFjayBPdmVyZmxvdy4gaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTIwMTg0OTkvaG93LXRvLXB1dC1sYWJlbHMtb3Zlci1nZW9tLWJhci1mb3ItZWFjaC1iYXItaW4tci13aXRoLWdncGxvdDIuCgo=